WKWebview的使用详解 以及WKWebview存在的坑点

2021-08-17  本文已影响0人  麟枫_Jack

虽然WKWebView是在Apple的WWDC 2014随iOS 8和OS X 10.10出来的,是为了解决UIWebView加载速度慢、占用内存大的问题。但是由于之前还要适配iOS7,所以就没有使用。现在项目都适配iOS 8以上了,所以就开始使用WKWebView了,但是发现在使用的时候有好多坑,希望这篇文章能带大家绕过坑,更好的使用WKWebView。
这篇文章主要介绍了以下问题,方便小伙伴们查阅:

  1. WKWebView的基本介绍和使用
  2. WKWebView和JavaScript的交互
  3. WKWebView 默认不弹出js的alert问题
  4. WKWebView 默认是不能识别电话号码
  5. WKWebView 拦截js通过window.open() 打开的窗口
  6. WKWebView解决文字显示太小问题

下面开始说第一个问题** WKWebView**基本使用

1.创建 跟UIWebview 一样

// 创建WKWebView
    WKWebView *webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
    // 设置访问的URL
    NSURL *url = [NSURL URLWithString:@"http://www.jianshu.com"];
    // 根据URL创建请求
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    // WKWebView加载请求
    [webView loadRequest:request];
    // 将WKWebView添加到视图
    [self.view addSubview:webView];

UIWebView和WKWebView的代理方法做一个对比
1.准备加载页面

UIWebViewDelegate: - webView:shouldStartLoadWithRequest:navigationType
 WKNavigationDelegate: - webView:didStartProvisionalNavigation:

2.内容开始加载

UIWebViewDelegate: - webViewDidStartLoad:
 WKNavigationDelegate: - webView:didCommitNavigation:

3.页面加载完成

UIWebViewDelegate: - webViewDidFinishLoad:
 WKNavigationDelegate: - webView:didFinishNavigation:

4.页面加载失败

UIWebViewDelegate: - webView:didFailLoadWithError:
 WKNavigationDelegate: - webView:didFailNavigation:withError:
 WKNavigationDelegate: - webView:didFailProvisionalNavigation:withError:

可以看到很简单,和UIWebView并没有多少差别,然而性能就刷刷刷的提上去了,是不是很爽呢?如果你只是简单的集成个Web页到App,这些已经够了。不过很多时候并没有那么简单,还需要处理各种东西,那么接着往后看。

接下来我们开始说第二个问题 WKWebView和JavaScript的交互

WKWebView和JavaScript的交互主要涉及到两个方面,一个是OC调用JavaScript ,另一个是 JavaScript 调用OC的方法,

在WebKit框架中,有WKWebView可以替换UIKit的UIWebView和AppKit的WebView,而且提供了在两个平台可以一致使用的接口。WebKit框架使得开发者可以在原生App中使用Nitro来提高网页的性能和表现,Nitro就是Safari的JavaScript引擎,WKWebView不支持JavaScriptCore的方式但提供message handler的方式为JavaScript与Native通信。(这个引自天狐博客,更多的与UIWebView或者WKWebView的交互方法可以在这里看到)。

1. OC调用JavaScript

OC调用JavaScrippt是相对来说比较简单的
只需要在调用的地方添加下面一句代码即可

//showAlert()是js里面的方法,这样就可以实现调用js方法
[self.webView evaluateJavaScript:@"showAlert('传参')" completionHandler:^(id item, NSError * _Nullable error) {
        // Block中处理是否通过了或者执行JS错误的代码
    }];

2. JavaScript 调用OC的方法,相对来说复杂一点

这地方需要两个配置,一个是OC代码的配置,另一个是JS代码的配置,下面先说一下OC代码的配置,细心的小伙伴可能已经发现了,创建WKWebView的时候,除了有- initWithFrame:方法外,还有一个高端的方法:- initWithFrame:configuration:方法。
#######OC代码的配置
1.配置 WKWebView

// 创建配置
      WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
      // 创建UserContentController(提供JavaScript向webView发送消息的方法)
      WKUserContentController* userContent = [[WKUserContentController alloc] init];
      // 添加消息处理,注意:self指代的对象需要遵守WKScriptMessageHandler协议,结束时需要移除
//NativeMethod 这个方法一会要与JS里面的方法写的一样
      [userContent addScriptMessageHandler:self name:@"NativeMethod"];
      // 将UserConttentController设置到配置文件
      config.userContentController = userContent;
      // 高端的自定义配置创建WKWebView
      WKWebView *webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds configuration:config];
      // 设置访问的URL
      NSURL *url = [NSURL URLWithString:@"http://www.jianshu.com"];
      // 根据URL创建请求
      NSURLRequest *request = [NSURLRequest requestWithURL:url];
      // WKWebView加载请求
      [webView loadRequest:request];
      // 将WKWebView添加到视图
      [self.view addSubview:webView];

2.实现协议方法

好了,现在万事俱备,只欠东风了。东风是什么呢,就是该在哪儿处理。可以看到WKScriptMessageHandler的协议里面只有一个方法,就是:

 - userContentController:didReceiveScriptMessage:

相信聪明的你已经猜到了。是的,就是在这个代理方法里面操作:如果JavaScript执行已经写好的:window.webkit.messageHandlers.NativeMethod.postMessage("就是一个消息啊");这行代码,这个代理方法就会走,并且会有个WKScriptMessage的对象,这个WKScriptMessage对象有个name属性,拿到之后你会发现,就是我们注册的NativeMethod这个字符串,这时候你就可以手动调用Native的方法了。如果有多个方法需要调用的话怎么办,看到JavaScript中postMessage()方法有一个参数了没有,可以根据这里的参数来区分调用原生App的哪个方法。

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
          // 判断是否是调用原生的
          if ([@"NativeMethod" isEqualToString:message.name]) {
              // 判断message的内容,然后做相应的操作
              if ([@"close" isEqualToString:message.body]) {

              }
          }
      }

2.JavaScript的配置

JavaScript调用Native的方法就需要前端和Native的小伙伴们配合了,需要前端的小伙伴在JS的方法中调用:

window.webkit.messageHandlers.方法名.postMessage(参数)

注意:

参数没有时传 (null) 这里是个坑点

第一:实现以上代码的时候不要忘记实现** WKScriptMessageHandler**协议
第二:上面将当前ViewController设置为MessageHandler之后需要在当前ViewController销毁前将其移除(dealloc方法),否则会造成内存泄漏。

//页面进入时创建
 -(void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    [self.webView.configuration.userContentController addScriptMessageHandler:self name:JS_goPageSelectClass];
    [self.webView.configuration.userContentController addScriptMessageHandler:self name:JS_goClasscardList];
}

//页面消失是移除
- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    [self.webView.configuration.userContentController removeScriptMessageHandlerForName:JS_goPageSelectClass];
    [self.webView.configuration.userContentController removeScriptMessageHandlerForName:JS_goClasscardList];
}

js区分Android和iOS的方法

           var u = navigator.userAgent;
            var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; //android终端
            var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //iOS终端

                if(isAndroid){
                    window.Android.alipayOrder();
                }
                if(isiOS){
                    window.webkit.messageHandlers.alipayOrder.postMessage(r);
                }

第三个问题 WKWebview 默认是不弹出js的alert 要想可以弹出alert 需要手动的设置代理实现

- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler

协议方法
具体的实现方法是,我们采用源生的UIAlertController 来实现弹出框,获取js里面的alert内容显示出来

- (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:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        completionHandler();
        NSLog(@"点击了取消按钮==%@",message);
    }])];
    [alertController addAction:([UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler();
        NSLog(@"点击了确定按钮==%@",message);
    }])];

    [self presentViewController:alertController animated:YES completion:nil];

}

问题四 : WKWebview 默认是不能识别电话号的,这里需要通过实现一个协议来实现拨打电话的功能

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler

具体的代码如下:

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
    NSURL *URL = navigationAction.request.URL;
    NSLog(@"获取到URL========%@",URL);
    NSString *scheme = [URL scheme];
    UIApplication *app = [UIApplication sharedApplication];
    // 打电话
    if ([scheme isEqualToString:@"tel"]) {
        if ([app canOpenURL:URL]) {
            [app openURL:URL];
            // 一定要加上这句,否则会打开新页面
            decisionHandler(WKNavigationActionPolicyCancel);
            return;
        }
    }
    decisionHandler(WKNavigationActionPolicyAllow);
}

注:对应html代码采用的是a标签

<a href="tel:18158711698">识别电话号码18158711698,进行拨打电话</a>

问题五: WKWebView 默认拦截open.window() 打开新的页面

- (WKWebView )webView:(WKWebView )webView createWebViewWithConfiguration:(WKWebViewConfiguration )configuration forNavigationAction:(WKNavigationAction )navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
会拦截到window.open()事件.
只需要我们在在方法内进行处理
if (!navigationAction.targetFrame.isMainFrame) {
[webView loadRequest:navigationAction.request];
}

问题六:WKWebView解决显示字体太小的问题

在使用WKWebView的时候,常常会碰到显示内容比实际css设置的样式不能正常显示,内容普遍的偏小。其实导致这样问题的根源是少了HTML5的meta标签。解决的办法可以在iOS端添加以下的内容,当然也可以让后台添加完整的HTML5的格式。如果要在iOS端指定字体的大小也是可以的(不推荐在客户端设置字体大小)。

 NSString *jScript = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";

 WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jScript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
WKUserContentController *wkUController = [[WKUserContentController alloc] init];
[wkUController addUserScript:wkUScript];

WKWebViewConfiguration *wkWebConfig = [[WKWebViewConfiguration alloc] init];
wkWebConfig.userContentController = wkUController;

_myWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0,CGRectGetMaxY(headerView.frame)+10, M_S.width,M_S.height - CGRectGetMaxY(headerView.frame) - 40) configuration:wkWebConfig];

客户端设置字体大小eg:

- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation{
    //修改字体大小 300%
    [ webView evaluateJavaScript:@"document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust= '200%'" completionHandler:nil];

    //    //修改字体颜色  #9098b8
    //    [ webView evaluateJavaScript:@"document.getElementsByTagName('body')[0].style.webkitTextFillColor= '#9098b8'" completionHandler:nil];

}

上一篇下一篇

猜你喜欢

热点阅读