iOS与H5的交互【WKWebView】

作者: 米拉_Recorder | 来源:发表于2017-04-06 20:52 被阅读6484次

H5因其及时响应的更新速度媲美着需求的速度和较高的趣味性受到越来越多的用户的青睐。目前,大多数的应用中都嵌入了H5。优点非常明显。那么在iOS应用中如何嵌入一个H5,并且和它进行交互就成了一个势必要掌握的技术了。本文我将结合我在项目中的一些需求整理出对应的技术点,仅供参考。

在iOS开发中,H5的嵌入可以通过UIWebView或者WKWebView。这两个都是继承UIView,来加载web数据的类。UIWebView是在iOS2的时候开始使用的。特点是加载速度慢,占用内存多,优化艰难。WKWebView是在iOS8苹果新推出的,加载速度快,占用内存较少,是一个不错的选择。如果想要比较两者的区别,您可以选择一个网页进行测试一下。鉴上所述,我们选择WKWebView进行开发。好了,废话不多说了。

1.WKWebView创建和加载

- (void)createWebView
{
    WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]
    // 根据需要去设置对应的属性
    WKWebView *webView = [[WKWebView alloc]initWithFrame:self.view.bounds configuration:config];
    webView.navigationDelegate = self;
    [self.view addSubview:webView];    

    NSURL *url = [NSURL URLWithString:self.strURL];
    [self loadWebViewWithURL:url];    // JS调用OC 添加处理脚本
    [self.webView.configuration.userContentController addScriptMessageHandler:self name:@"Share"];
}

2.JS调用OC代码。

  [self.webView.configuration.userContentController addScriptMessageHandler:self name:@"Share"];

这是利用WKWebView的一个新特性MessageHandler来处理JS调用原生方法。要实现JS调用iOS原生方法,步骤见下。

  • 添加<WKScriptMessageHandler>协议。让控制器成为MessageHandler的代理对象。
  • 对于监听的方法名要和JS开发的人商量好。这里我们监听的是Share方法,对于JS开发的人员必须要以以下方式写。
   window.webkit.messageHandlers. Share.postMessage(null)
  • 实现协议方法。在这个方法里message参数有一个属性body。message.body就是JS传过来的参数,可以是字符串,可以是数组,也可以是字典。通过message.name判断可以知道监听的是JS的哪个方法。
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{    
    if ([message.name isEqualToString:@"Share"]) {
             //TODO
    }
}

至此,JS调用OC代码就已完结。是不是很简单。另外,我在网上也看到了不一样的处理方式。大家可以参考WebViewJavascriptBridge我觉得写的比较清楚。本人还没有尝试过这种,如果都尝试过的宝宝能不能分享一下两者的优缺点啊。

3. OC调用JS代码。

     [self.webView evaluateJavaScript:@"show()" completionHandler:^(id _Nullable response, NSError * _Nullable error) {
                //TODO
      }];

相信代码已经看得很清楚啦。show()就是JS写的方法,这个方法可传可不传参数,具体依实际情况而定。另外关于UIWebView和JS的交互,以下部分仅供参考。

    JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    context[@"Share"] = ^() {
        NSArray *args = [JSContext currentArguments];
        dispatch_async(dispatch_get_main_queue(), ^{
                //TODO
        });

4. 关于<WKNavigationDelegate>

网页加载开始,结束,失败这几个都特别简单,我就不赘述了。说一下下面这个协议方法,这个方法发生在页面跳转中。WKNavigationActionPolicy是一个枚举,WKNavigationActionPolicyAllow表示允许跳转,WKNavigationActionPolicyCancel表示取消跳转。对了,这里还有一个补充: scrollView嵌套网页和原生view,原生view要根据网页的高度来布局。我看到不少的电商应用都有这种布局,但在算高度上会有各种问题,不知道你们有遇见过?

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
    NSString *url = navigationAction.request.URL.absoluteString;
    if(![url isEqualToString:self.strURL]) {
          // 页面跳转
    }
    decisionHandler(WKNavigationActionPolicyAllow);
}

5. 关于< WKUIDelegate >

不知道您有没有遇见过JS写的alert()框在iOS上不弹出。那么您有没有实现这些协议方法呢。

/// 创建一个新的WebView
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures;
/// 输入框
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler;
/// 确认框
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler;
/// 警告框
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler;

6. 获取网页标题,网页加载进度和加载状态

这是通过KVO的方式进行监听的。您可以点击进WKWebView的内部看一下,他们每个属性上面都有很长的解释,你不难发现这一段。举一个获取标题的例子。其他的类似。别忘了,KVO监听在dealloc中移除监听者哦。


8E6F0A58-609E-4093-BD65-F51A10D1703D.png
    [self.webView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:NULL];

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{  
    if ([keyPath isEqualToString:@"title"]) {
        if (object == self.webView) {
            if(self.navigationController) 
                self.navigationItem.title = self.webView.title;
        }
    }
    else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

以上就是我个人对WKWebView的一些理解。demo就不奉上了,这个要服务端配合。因为我写的H5基本见不了人,哈哈,我会努力的!

相关文章

网友评论

  • cef84bbc10d6:想问下博主,h5 更新后,WKWebView 没有及时更新,有办法解决吗
    米拉_Recorder:@码农六叔 你好,这是清除缓存的代码,您可以试试
    if ([[UIDevice currentDevice].systemVersion floatValue] >= 9.0){
    NSSet *websiteDataTypes
    = [NSSet setWithArray:@[
    WKWebsiteDataTypeDiskCache,
    WKWebsiteDataTypeMemoryCache,
    ]];

    NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0];
    [[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes modifiedSince:dateFrom completionHandler:^{

    }];
    }
    优米诺:你说的是缓存吧!我博客里有,你可以看看
  • 时间shiwo9:wkwebview调用https的时候好像有问题吧???
    时间shiwo9:@MiRaDing wkwebview没法直接加载https,需要使用js加载,而且调试的时候也是各种麻烦的说:sob:
    米拉_Recorder:@时间shiwo9 我们公司用的还是http,你调的有什么问题呢?
  • yunFeng: _rowHeight = [[webView stringByEvaluatingJavaScriptFromString:@"document.body.offsetHeight"] floatValue];
    这是webview 的写法 以前做项目时遇到过这个问题 尝试了好久 才从网上找到的资料的

    [self.wkWebView evaluateJavaScript:@"document.body.offsetHeight" completionHandler:^(id _Nullable response, NSError * _Nullable error) {

    }];
    wk的这样写 应该是可以的没有试验过 麻烦作者测试下 另外获取title的 可以直接在webview 加载完结束 直接获取就可以了把 self.webview.title
  • 广锅锅:@优米诺 所以说,removeAllUserScripts这个方法应该放在dealloc方法里执行对吧。
    优米诺:@广锅锅 有时候多看看头文件好处多多:smile:
    广锅锅:@优米诺 你好,我发现我错了,WKUserContentController有4个方法,
    1. - (void)addUserScript:(WKUserScript *)userScript;
    2. - (void)removeAllUserScripts;
    3. - (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;
    4. - (void)removeScriptMessageHandlerForName:(NSString *)name;

    其中,1&2是一对方法,而3&4是另一对方法,他们没有任何联系。而我之前为了偷懒且顾相似名而思义,没有好好看方法解释。我以为3可以和2配对使用,以为3注册的所有handle都可以用2的一句话来注销。
    而经过你这次的提醒,我去研究也试验了一下,发现2并不能注销掉3注册的任何handle,你说的对,2和1是对应的。3和4是对应的。
    优米诺:你说的这个对应的方法是- (void)addUserScript:(WKUserScript *)userScript;这是增加一个JS文件吧
  • 优米诺:我觉得你在调用addScriptMessageHandler:这个方法后最好看一下当前加载webkit的控制器消失时有没有在内存中被销毁
    米拉_Recorder:@优米诺 没有,是放在viewDidLoad中的。是的,当前界面没有二级界面。这样有什么问题吗?写博客一个很重要的好处就是这个,可以接受更多的思想,认识自己的不足。我很开心收到你的评论。
    优米诺:@MiRaDing 那你的addScriptMessageHandler难道是放在viewWillAppear吗?这样除非你当前页面没有二级页面了才可以这样做,建议你使用代理来做。没别的意思,就是写博客建议你尽量写全写完整
    米拉_Recorder:在viewWillDisappear中removeScriptMessageHandlerForName即可
  • Coder亚瑟士:支持一下,我们的还要适配iOS7:sweat:
    米拉_Recorder:哈哈 WebViewJavascriptBridge你值得拥有
  • 漫步的小蚂蚁:楼主,可以用用webviewjavascripbridge ,这个更简单
    米拉_Recorder:@漫步的小蚂蚁 嗯嗯 下次尝试用一下
  • 墨源为水:写的非常全面,赞赞赞
    米拉_Recorder:@墨源为水 过奖,共同学习。
  • xiAo__Ju:还可以用捕获URL的方法
    wkw0913:https://www.jianshu.com/p/628b03538955
    米拉_Recorder:@xiAo__Ju 嗯嗯,页面跳转就用的是捕获URL的方式。

本文标题:iOS与H5的交互【WKWebView】

本文链接:https://www.haomeiwen.com/subject/uhvqattx.html