反馈请联系hertz@hertzwang.com,谢谢
前言:用
Safari的访问一个网址,然后将网址添加到主屏幕,这时会在主屏幕上生成一个书签(带有Icon和标题),点击该书签后会访问指定的网址,在指定的网址中使用“scheme://”的方式打开应用,关于iOS 14上的使用参考底部的iOS 14 示例
使用远程Web
使用Safari打开链接并添加到主屏幕从而得到桌面快捷方式,通过window.navigator.standalone来区分状态,在Safari中加载时显示引导信息,独立全屏运行(点击主屏幕的快捷图标)时使用scheme来打开App,例如:scheme://modules?id=1
HTML示例代码:
| 1 | <!DOCTYPE html> | 
- apple-mobile-web-app-capable:仅支持Safari浏览器,以独立进程运行时全屏显示(Safari中输入网址打开链接称之为浏览器内打开,添加到主屏幕后,在主屏幕中点击生成的书签运行称之为独立进程运行)
- apple-mobile-web-app-status-bar-style:仅支持Safari浏览器,设置状态栏颜色,apple-mobile-web-app-capable为YES时且独立进程运行时生效
- apple-touch-icon:书签图标
- title:标签内容为书签的名称
- window.navigator.standalone:独立进行状态
参考:
iOS示例代码
| 1 | NSString *scheme = [NSString stringWithFormat:@"scheme://modules?id=%@", xxx]; // 组装Scheme | 
使用本地Web
不支持iOS 14,在iOS 14以下可以使用开启本地服务的方式来实现,其原理为本地开启HTTP Server服务后打开网页,然后重定向到data:text/html,添加到主屏幕的其实就是 数据形式的HTML ,这种数据形式可以独立运行不依赖服务,从而实现完全本地打开应用。
本地HTTP Server
使用第三方库 CocoaHTTPServer 或 GCDWebServer 来开启本地HTTP Server
网页部分
使用两个网页模板 Index.html 和 Content.html,先替换 Content.html 中的 %ShortcutIcon% 和 %ShortcutName%,再替换 Index.html 中的 %Content%
Index.html
| 1 | <!DOCTYPE html> | 
Content.html
| 1 | <!DOCTYPE html> | 
iOS 部分
启动 HTTP Server,访问 http://localhost:port/export.html
iOS 14 示例
- 依赖三方库 - GCDWebServer
- 新建 - AddToHomeScreenContent.html,代码如下:- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41- <!DOCTYPE html> 
 <html>
 <head>
 <meta name="apple-mobile-web-app-capable" content="yes">
 <meta name="apple-mobile-web-app-status-bar-style" content="black">
 <meta content="text/html charset=UTF-8" http-equiv="Content-Type" />
 <meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no" />
 <link rel='apple-touch-icon' href='%ShortcutIcon%'><!-- base64的图片 -->
 <title>%ShortcutName%</title>
 </head>
 <body>
 <a href="%AppScheme%" id="startApp" style="display:none"></a>
 <div id="msg">
 <div class="main">
 <div>点击下方工具栏上的</div>
 <div>并点击添加到主屏幕</div>
 </div>
 <img class="tips" src="%TipsImage%"/>
 </div>
 </body>
 <script>
 if (window.navigator.standalone == true) {
 document.getElementById("msg").innerHTML= '';
 var lnk = document.getElementById("startApp").click();
 }
 </script>
 <style>
 .main {
 color: #333;
 text-align: center;
 width: auto;
 }
 .tips {
 width: %TipsImageWidth%;
 position: absolute;
 left: %TipsImageLeft%;
 bottom: 10px;
 }
 </style>
 </html>
- 新建 - HttpServerUtil : NSObject- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15- @interface HttpServerUtil : NSObject 
 + (instancetype)shared;
 /// 添加到主屏幕
 /// @param image 桌面图标,为nil时使用应用Icon
 /// @param name 桌面显示的名称
 /// @param scheme 当前应用scheme
 + (void)openSafariWithIcon:(UIImage * _Nullable)image name:(NSString *)name scheme:(NSString *)scheme;
 - (void)start;
 - (void)stop;
 @end- 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138- #import "HttpServerUtil.h" 
 #import <GCDWebServer.h>
 #import <GCDWebServerDataResponse.h>
 @interface HttpServerUtil ()
 @property (nonatomic, strong) GCDWebServer *webServer;
 @property (nonatomic, strong) NSString *base64ImageString;
 @property (nonatomic, strong) NSString *appName;
 @property (nonatomic, strong) NSString *appScheme;
 @property (nonatomic, strong) UIImage *appIconImage;
 @property (nonatomic, strong) NSURL *httpServerURL;
 @end
 @implementation HttpServerUtil
 #pragma mark - Override
 - (instancetype)init
 {
 self = [super init];
 if (self) {
 __weak typeof(self) weak_self = self;
 [self.webServer addDefaultHandlerForMethod:@"GET"
 requestClass:[GCDWebServerRequest class]
 processBlock:^GCDWebServerResponse * _Nullable(__kindof GCDWebServerRequest * _Nonnull request) {
 if (weak_self) {
 __strong typeof(weak_self) self = weak_self;
 return [self _configResponse];
 } else {
 return nil;
 }
 }];
 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_applicationWillEnterForeground) name:UIApplicationWillEnterForegroundNotification object:nil];
 }
 return self;
 }
 #pragma mark - Public
 + (instancetype)shared {
 static HttpServerUtil *instance;
 static dispatch_once_t onceToken;
 dispatch_once(&onceToken, ^{
 instance = [[HttpServerUtil alloc] init];
 });
 return instance;
 }
 + (void)openSafariWithIcon:(UIImage *)image name:(nonnull NSString *)name scheme:(nonnull NSString *)scheme {
 HttpServerUtil *serverUtil = [HttpServerUtil shared];
 serverUtil.base64ImageString = [serverUtil _base64WithImage:image icon:YES];
 serverUtil.appName = name;
 serverUtil.appScheme = scheme;
 [serverUtil start];
 [[UIApplication sharedApplication] openURL:serverUtil.httpServerURL options:@{} completionHandler:^(BOOL success) {
 
 }];
 }
 - (void)start {
 if ([self.webServer isRunning]) {
 return;
 }
 NSUInteger port = 12315;
 [self.webServer startWithPort:port bonjourName:nil];
 self.httpServerURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://127.0.0.1:%lu", port]];
 }
 - (void)stop {
 if (![self.webServer isRunning]) {
 return;
 }
 [self.webServer stop];
 }
 #pragma mark - Private
 - (GCDWebServerResponse *)_configResponse {
 // 设置名称、Icon、Scheme
 NSString *contentHtmlPath = [[NSBundle mainBundle] pathForResource:@"AddToHomeScreenContent" ofType:@"html"];
 NSMutableString *contentHtml = [[NSMutableString alloc] initWithContentsOfFile:contentHtmlPath encoding:NSUTF8StringEncoding error:NULL];
 NSString *base64TipsImage = [self _base64WithImage:[UIImage imageNamed:@""] icon:NO];
 NSString *tipsImageWidth = [NSString stringWithFormat:@"%lfpx", [[UIScreen mainScreen] bounds].size.width - 15 * 2];
 NSString *tipsImageLeft = [NSString stringWithFormat:@"%lfpx", 15.0f];
 NSDictionary *variables = @{
 @"ShortcutIcon" : self.base64ImageString,
 @"ShortcutName" : self.appName,
 @"AppScheme" : self.appScheme,
 @"TipsImage" : base64TipsImage,
 @"TipsImageWidth" : tipsImageWidth,
 @"TipsImageLeft" : tipsImageLeft,
 };
 [variables enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSString* value, BOOL* stop) {
 [contentHtml replaceOccurrencesOfString:[NSString stringWithFormat:@"%%%@%%", key] withString:value options:0 range:NSMakeRange(0, contentHtml.length)];
 }];
 NSString *urlStringEncode = [contentHtml stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
 NSString *html = [NSString stringWithFormat:@"data:text/html;charset=utf-8,%@", urlStringEncode];
 GCDWebServerResponse *rsp = [GCDWebServerResponse responseWithRedirect:self.httpServerURL permanent:YES];
 [rsp setValue:html forAdditionalHeader:@"Location"]; // 设置URLEncoded的html
 return rsp;
 }
 - (NSString *)_base64WithImage:(UIImage * _Nullable)image icon:(BOOL)isIcon {
 if (isIcon) {
 image = (image ? [image sd_resizedImageWithSize:CGSizeMake(180, 180) scaleMode:SDImageScaleModeAspectFill] : self.appIconImage);
 }
 NSData *data = UIImagePNGRepresentation(image);
 NSString *encodedImageStr = [data base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
 NSString *result = [NSString stringWithFormat:@"data:image/png;base64,%@", encodedImageStr];
 return result;
 }
 - (void)_applicationWillEnterForeground {
 [self stop];
 }
 #pragma mark - Getter
 - (GCDWebServer *)webServer {
 if (_webServer == nil) {
 _webServer = [[GCDWebServer alloc] init];
 }
 return _webServer;
 }
 - (UIImage *)appIconImage {
 if (_appIconImage == nil) {
 NSDictionary *infoDict = [[NSBundle mainBundle] infoDictionary];
 NSArray *iconsArr = infoDict[@"CFBundleIcons"][@"CFBundlePrimaryIcon"][@"CFBundleIconFiles"];
 NSString *iconLastName = [iconsArr lastObject];
 _appIconImage = [UIImage imageNamed:(iconLastName ?: @"AppIcon")];
 }
 return _appIconImage;
 }
 @end
- 使用 - [HttpServerUtil openSafariWithIcon:nil name:@"xxx" scheme:@"xx://"];