WKWebView替换UIWebView

1.引入头文件

#import <WebKit/WebKit.h>

2.先使用WKWebView基本方法写个例子,加载baidu页面,写法跟UIWebView类似

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
    
    _webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, self.view.width, self.view.height) configuration:config];
    [self.view addSubview:_webView];
    
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://www.baidu.com"]];
    [_webView loadRequest:request];
}

用同样的方法,用UIWebView也写了一个页面做对比,发现差距好大,WKWebView内存是25M,而UIWebView是98M。之前没发现UIWebView内存会占如何之大,WKWebView看来很值!!!

3.监听加载的进度,因为没有“加载进度”相关的delegate回调

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self.webView addObserver:self
                   forKeyPath:@"loading"
                      options:NSKeyValueObservingOptionNew
                      context:nil];
    [self.webView addObserver:self
                   forKeyPath:@"title"
                      options:NSKeyValueObservingOptionNew
                      context:nil];
    [self.webView addObserver:self
                   forKeyPath:@"estimatedProgress"
                      options:NSKeyValueObservingOptionNew
                      context:nil];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if ([keyPath isEqualToString:@"loading"]) {
        NSLog(@"loading--%d", _webView.loading);
    } else if ([keyPath isEqualToString:@"title"]) {
        NSLog(@"title--%@", _webView.title);
    } else if ([keyPath isEqualToString:@"estimatedProgress"]) {
        NSLog(@"progress--%lf", _webView.estimatedProgress);
    }
}


- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [_webView removeObserver:self forKeyPath:@"loading"];
    [_webView removeObserver:self forKeyPath:@"title"];
    [_webView removeObserver:self forKeyPath:@"estimatedProgress"];
}

记得最后移除KVO

日志如下:

2017-08-16 16:35:33.833 TestSSSSS[22084:917889] progress--0.300000
2017-08-16 16:35:33.845 TestSSSSS[22084:917889] title--百度一下
2017-08-16 16:35:34.165 TestSSSSS[22084:917889] progress--0.409694
2017-08-16 16:35:34.454 TestSSSSS[22084:917889] progress--0.845262
2017-08-16 16:35:34.472 TestSSSSS[22084:917889] progress--1.000000
2017-08-16 16:35:34.473 TestSSSSS[22084:917889] loading--0

在加载结束前取得title,最后结束时progress为1,loading为NO

4.为了方便测试,我自己写了一个html文件,页面上有个按钮,然后点击按钮弹出alert,代码如下

<html>
<head>
<script>
function postMsg() {
    alert("hehe");
}
</script>
</head>
<body>

<input type='button' value='method1' onclick='postMsg()'/>
</body>
</html>

但是奇怪的是alert怎么也弹不出来,听说WKWebView坑很多,要慎入,现在终于碰到第一个了,
解决方法如下:

  • 1.实现两个代理
_webView.UIDelegate = self;
_webView.navigationDelegate = self;
  • 2.具体实现三个方法
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:([UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler();
    }])];
    [self presentViewController:alertController animated:YES completion:nil];
    
}

- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler{
    //    DLOG(@"msg = %@ frmae = %@",message,frame);
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:([UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(NO);
    }])];
    [alertController addAction:([UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(YES);
    }])];
    [self presentViewController:alertController animated:YES completion:nil];
}
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:prompt message:@"" preferredStyle:UIAlertControllerStyleAlert];
    [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
        textField.text = defaultText;
    }];
    [alertController addAction:([UIAlertAction actionWithTitle:@"完成" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(alertController.textFields[0].text?:@"");
    }])];
    
    [self presentViewController:alertController animated:YES completion:nil];
}

5.js调用oc方法 重点哦!

  • js里调用方法
 window.webkit.messageHandlers.message.postMessage("hahahaha");
  • oc使用方法, config里的userContentController专门处理js的调用
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
   config.userContentController = [[WKUserContentController alloc] init];
   [config.userContentController addScriptMessageHandler:self name:@"message"];
  • 接收js事件用法
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
  if ([message.name isEqualToString:@"message"]) {
      NSLog(@"body--%@", message.body);
      //处理js事件...
  }
}

这种写法完美的实现了js与oc的无缝连接,有点类似于服务器用的redis, 通过webKit的消息进行传递。

  • remove scriptMessage, 最后要移除,千万别忘了,否则内存泄漏。

  • (void)dealloc {
    [_webView.configuration.userContentController removeScriptMessageHandlerForName:@"message"];
    }

6.WKWebView加载过程代理方法:

 
 #pragma mark WKNavigationDelegate
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
    NSLog(@"start");
}

- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
    NSLog(@"commit");
}

- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    NSLog(@"finish");
}

- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error {
    NSLog(@"fail");
}

7.页面跳转代理方法, 可用于拦截URL

// 接收到服务器跳转请求之后调用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
    NSLog(@"recieve erdirect");
}

// 在收到响应后,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
    decisionHandler(WKNavigationResponsePolicyAllow);
}
// 在发送请求之前,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    decisionHandler(WKNavigationActionPolicyAllow);
}

8.OC调用JS方法

WKWebView自带的evaluateJavaScript方法

[_webView evaluateJavaScript:@"ocInvockMsg()" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
            //result 是方法的返回值
            NSLog(@"result-%@", result);
        }];
WebViewJavascriptBridge第三方库 github上有9000+星,但是我觉得它适应于UIWebView,WKWebView感觉不太需要,而且WebViewJavascriptBridge需要在html添加初始化代码,侵入性太强,代码如下:
<script type="text/javascript">
    
    
    //这是必须要写的,用来初始化一些设置
    function setupWebViewJavascriptBridge(callback) {
        if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
        if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
        window.WVJBCallbacks = [callback];
        var WVJBIframe = document.createElement('iframe');
        WVJBIframe.style.display = 'none';
        WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__';
        document.documentElement.appendChild(WVJBIframe);
        setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
    }


//这也是固定的, OC 调JS , 需要给OC调用的函数必须写在这个函数里面
setupWebViewJavascriptBridge(function(bridge) {
                             
                             bridge.registerHandler('testJSFunction', function(data, responseCallback) {
                                                    alert('JS方法被调用:'+data);
                                                    responseCallback('js执行过了');
                                                    })
                             
                             
                             })
                             
                             
                             //这个 shareClick 就是 原生那边 注入的函数 , 通过这个方法来调用原生 和传值
                             //parmas 是JS 给OC的数据 , response 是 OC函数被调用之后 再 告诉JS 我被调用了
                             //比如微信分享,给Dic给原生 , 原生分享成功后再把结果回调给JS 进行处理
                             function shareClick() {
                                 var params = {'title':'测试分享的标题','content':'测试分享的内容','url':'http://www.baidu.com'};
                                 WebViewJavascriptBridge.callHandler('shareClick',params,function(response) {
                                                                     
                                                                     console.log(response);
                                                                     
                                                                     
                                                                     });
                             }



</script>

webBridge初始化及注册用于js调用oc的函数

    _bridge = [WebViewJavascriptBridge bridgeForWebView:_webView];
    [_bridge setWebViewDelegate:self];
    [WebViewJavascriptBridge enableLogging];
    [_bridge registerHandler:@"shareClick" handler:^(id data, WVJBResponseCallback responseCallback) {
        NSLog(@"share---");
    }];

oc调用js的函数,如下:

[_bridge callHandler:@"testJSFunction" data:@"aa" responseCallback:^(id responseData) {
            NSLog(@"responseData--%@", responseData);
        }];

综上所述,WebViewJavascriptBridge使用非常方便,但是前台html的业务量上升,不仅要添加初始化方法,并在刚开始时调用一次,而且还要在html里使用registerHandler和callHandler这两个方法来注册和调用方法,感觉Html里侵入性太强了,我要是做前端的我就疯了,因为它的代码还在网站上显示啊,不光是用在iOS/android上呀,而且你们iOS出了个新框架,让我前端跟着变,你当我傻???

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

推荐阅读更多精彩内容