14-3 iOS 与H5

执行一段js代码

#pragma mark --  收集JS页面传来的图片及添加图片点击事件
-(void)getImagesFromJSAndClickImgEvent:(WKWebView *)webView{
    //这里是JS,主要目的: - 获取H5图片的url
    static  NSString * const jsGetImages =
    @"function getImages(){\
    var objs = document.getElementsByTagName(\"img\");\
    var imgScr = '';\
    for(var i=0;i<objs.length;i++){\
    imgScr = imgScr + objs[i].src + '+';\
    };\
    return imgScr;\
    };";
    WS(weakSelf);
    [self.webView evaluateJavaScript:jsGetImages completionHandler:^(id _Nullable result, NSError * _Nullable error) {
        
    }];
    
    [self.webView evaluateJavaScript:@"getImages()" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
        SS(strongSelf);
        strongSelf.mUrlArray = [NSMutableArray arrayWithArray:[result componentsSeparatedByString:@"+"]];
        if (strongSelf.mUrlArray.count >= 2) {
            [strongSelf.mUrlArray removeLastObject];
        }
    }];
    
    [self.webView evaluateJavaScript:@"function registerImageClickAction(){\
     var imgs=document.getElementsByTagName('img');\
     var length=imgs.length;\
     for(var i=0;i<length;i++){\
     img=imgs[i];\
     img.onclick=function(){\
     window.location.href='image-preview:'+this.src}\
     }\
     }" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
        
    }];
    
    [self.webView evaluateJavaScript:@"registerImageClickAction();" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
        
    }];
    
}

计算高度:
    //加载web页面数据
    NSString *fullContent = @"<html><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1,maximum-scale=1, user-scalable=no\" /><link href=\"http://apps.bdimg.com/libs/bootstrap/3.3.4/css/bootstrap.css\" rel=\"stylesheet\"></head><body><div style=\"font-size:14px;width:100%;line-height:1.6;word-break;break-all;word-wrap:break-word;padding-bottom:20px;color: #999999;\">'+temp+'</div><div id=\"testDiv\" style = \"height:0; width:100px\"></div></body></html>";
    //防止/n不换行,替换标签
    NSString *changeContent = [model.content stringByReplacingOccurrencesOfString:@"\n" withString:@"<br>"];
    fullContent = [fullContent stringByReplacingOccurrencesOfString:@"'+temp+'" withString:STRING_NIL(changeContent)];
    fullContent = [fullContent stringByReplacingOccurrencesOfString:@"<b>" withString:@"<b style=\"color: #333333; font-size: 16px;\">"];
    fullContent = [fullContent stringByReplacingOccurrencesOfString:@"display:block;width:100%" withString:[NSString stringWithFormat:@"display:block;width:%fpx",Screen_W - 40]];
    
    [self.webView loadHTMLString:fullContent baseURL:nil];
   
    
    WS(weakSelf);
    [self.webView evaluateJavaScript:@"document.getElementById(\"testDiv\").offsetTop" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
        SS(strongSelf);
        //获取页面高度,并重置webview的frame
        strongSelf.model.height = [result doubleValue] + 84;
    }];

2.注入监听方法不是方法

    // WKWebView的配置
    WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
    [configuration.userContentController addScriptMessageHandler:self name:@"userSelectionString"];
    // js 方法注入
    NSString *printContent = @"document.addEventListener('selectionchange', function () {window.webkit.messageHandlers.userSelectionString.postMessage(window.getSelection().toString());})";
    WKUserScript *userScript = [[WKUserScript alloc] initWithSource:printContent injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
    [configuration.userContentController addUserScript:userScript];

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    NSLog(@"userContentController %@",message.body);
    NSLog(@"userContentController %@",message.name);
}

3.获取文字

    NSString *lJs2 = @"document.documentElement.innerText"; //根据标识符获取不同内容

4.获取WebView加载的HTML

[webView evaluateJavaScript:@"document.getElementsByTagName('html')[0].innerHTML" completionHandler:^(id result, NSError * _Nullable error) {
    NSString *html = result;
    NSLog(@"%@", html);
}];

5.JS调用iOS的代码

    WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
    [configuration.userContentController addScriptMessageHandler:self name:@"userSelectionString"];

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {

    NSString *body = message.body;
    if ([NSString isBlankString:body]) {
        
    } else {
        
    }
}

js方法:window.webkit.messageHandlers.userSelectionString.postMessage(window.getSelection().toString())
最好是传json

5.wkwebview 去掉剪切板

#import "HDPlayBackMuluWebView.h"

BOOL wel_canPerformAction(id self,  SEL _cmd, SEL arg1, id arg2) {
    return NO;
}


@implementation HDPlayBackMuluWebView


/// iOS 10.0 调用
+ (void)load {
    
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Method  m = class_getInstanceMethod(NSClassFromString(@"WKContentView"), NSSelectorFromString(@"canPerformAction:withSender:"));
        
        class_addMethod(NSClassFromString(@"WKContentView"), NSSelectorFromString(@"wel_canPerformAction:withSender:"), (IMP)wel_canPerformAction, method_getTypeEncoding(m));
        
        Method m1 = class_getInstanceMethod(NSClassFromString(@"WKContentView"), NSSelectorFromString(@"canPerformAction:withSender:"));
        Method m2 = class_getInstanceMethod(NSClassFromString(@"WKContentView"), NSSelectorFromString(@"wel_canPerformAction:withSender:"));
        method_exchangeImplementations(m1,m2);
    });
}

- (BOOL)wel_canPerformAction:(SEL)arg1 withSender:(id)arg2 {
    return NO;
}


/// iOS 10.0 的系统不可以用
- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
    return NO;
}
@end

6优化工作

1.白屏
2.cookie
3.秒开:缓存和预加载

1.使用本地资源 文件

1.这边实现了一个 webview 缓冲池的方案,在 App 启动的时候就初始化了,在需要打开网页的时候直接从缓冲池里面去取 webview 就行
2.自定义拦截请求 setURLSchemeHandler 把能缓存的都缓存[html 拦截/js || css 文件/]下来 并且可以设置缓存的策略 内存缓存 和 硬盘缓存. 使用请求头获取到请求的资源:Accept
3.在 webview 初始化的同时并行去请求数据?这个怎么做 使用js和H5的交互

2.使用缓存

1.页面即将白屏的时候WKNavigationDelegate会回调一个方法 我们在这里执行reload方法
2.在跳转其他页面占有大量内存的时候。在viewwillapple执行reload方法

3.cookie问题 服务器返回给iOS iOS请求在带给服务器 或者H5 Cookie最常用的也就是维持登录状态了

cookie 我们登陆成功后获取到cookie。然后首次打开 webview的时候携带上
存储的时候我们需要区分iOS11 和 iOS 11 之前

iOS11之前
1.iOS和js交互 通过 document.cookie 设置 Cookie 解决后续页面(同域)Ajax、iframe 请求的 Cookie 问题
2.拼接在header里面 设置请求头  不能被js读取到
通过key-Value构造一个cookie,WKWebView loadRequest 前,在 request header 中设置 Cookie, 解决首个请求 Cookie 带不上的问题,

iOS11之后 将cookie存入到WKHTTPCookieStore里面
    /// 发送请求之前
    if (@available(iOS 11.0, *)) {
        WKHTTPCookieStore *httpCookieStore = webView.configuration.websiteDataStore.httpCookieStore;
        for (NSHTTPCookie *cookie in cookies) {
            [httpCookieStore setCookie:cookie completionHandler:^{
                            
            }];
        }
    } else {
        // Fallback on earlier versions
    }

获取cookie是从登陆成功的接口中获取的,这个时候的cookie是被同步到了[[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]中,其实整个app的生命周期里,所有的通过网络请求用到的cookie都会被同步到这个单例中,由它进行管理。
然后保存起来 


获取cookie 存起来:1)从网站返回的 response headerfields 中获取。(2)通过调用js的方法获取 cookie。
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
    NSHTTPURLResponse *response = (NSHTTPURLResponse *)navigationResponse.response;
    NSArray *cookies =[NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:response.URL];
    NSLog(@"\n====================================\n");
    //读取wkwebview中的cookie 方法1
    for (NSHTTPCookie *cookie in cookies) {
        //        [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
        NSLog(@"wkwebview中的cookie:%@", cookie);
    }
    NSLog(@"\n====================================\n");
    //读取wkwebview中的cookie 方法2 读取Set-Cookie字段
    NSString *cookieString = [[response allHeaderFields] valueForKey:@"Set-Cookie"];
    NSLog(@"wkwebview中的cookie:%@", cookieString);
    NSLog(@"\n====================================\n");
    //看看存入到了NSHTTPCookieStorage了没有
    NSHTTPCookieStorage *cookieJar2 = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    for (NSHTTPCookie *cookie in cookieJar2.cookies) {
        NSLog(@"NSHTTPCookieStorage中的cookie%@", cookie);
    }
    NSLog(@"\n====================================\n");

    decisionHandler(WKNavigationResponsePolicyAllow);
}

通过 JS 获取 cookie
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation
{
    
    [webView evaluateJavaScript:[NSString stringWithFormat:@"document.cookie"] completionHandler:^(id _Nullable response, NSError * _Nullable error) {
        if (response != 0) {
            NSLog(@"\n\n\n\n\n\n document.cookie%@,%@",response,error);
        }
    }];
}


document.cookie 的方法获取 cookie并不支持跨越获取
1.加载一个本地为空的html,域名指向你的第一次加载的url的域名。
    if ([response.URL.scheme.lowercaseString containsString:@"http"]) {
        NSArray *cookies =[NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:response.URL];
        if (@available(iOS 11.0, *)) {
            //浏览器自动存储cookie
        }else
        {
            //存储cookies
            dispatch_sync(dispatch_get_global_queue(0, 0), ^{
                
                @try{
                    //存储cookies
                    for (NSHTTPCookie *cookie in cookies) {
                        [weakSelf.webView insertCookie:cookie];
                    }
                }@catch (NSException *e) {
                    NSLog(@"failed: %@", e);
                } @finally {
                    
                }
            });
        }
        
    }

性能优化 我们本地加载数据 + 加载web页面。然后数据展示:
NB啊这个人

iOS 端 h5 页面秒开优化实践

https://www.jianshu.com/p/cd0d819b9851

iOS UIWebView 和 WKWebView 的 cookie 获取,设置,删除
这里讨论了 H5 页面首屏启动时间的优化,上述优化过后,基本上耗时只剩 webview 本身的启动/渲染机制问题了,这个问题跟后续的响应流畅度的问题一起属于另一个优化范围,就是类 RN / Weex 这样的方案,有机会再探讨。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352

推荐阅读更多精彩内容