iOS WKWebView的使用

2018-07-24  本文已影响0人  光之盐汽水

在开发过程中,iOS 中实现加载 web 页面主要有两种控件,UIWebView 和 WKWebview,两种控件对应具体的实现方法不同。WKWebView 是苹果在iOS 8中引入的新组件,目的是提供一个现代的支持最新Webkit功能的网页浏览控件,摆脱过去 UIWebView的老、旧、笨,特别是内存占用量巨大的问题。它使用与Safari中一样的Nitro JavaScript引擎,大大提高了页面js执行速度。

相比于UIWebView的优势:

在性能、稳定性、占用内存方面有很大提升;
允许JavaScript的Nitro库加载并使用(UIWebView中限制)
增加加载进度属性:estimatedProgress,不用在自己写假进度条了
支持了更多的HTML的属性。

使用方法:

1、导入头文件:#import <WebKit/WebKit.h>

2、创建WKWebView:

@property (nonatomic, strong) WKWebView *webView;
#pragma mark - webView
- (WKWebView *)webView {
    if (!_webView) {
        _webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
        _webView.backgroundColor = [UIColor whiteColor];
        _webView.navigationDelegate = self;
        _webView.UIDelegate = self;
        self.webView.allowsBackForwardNavigationGestures = YES;
    }
    return _webView;
}

3、设置代理<WKNavigationDelegate, WKUIDelegate>

// 页面开始加载时调用
-(void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{
    self.progressView.hidden = NO;
}
// 当内容开始返回时调用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation{
}
// 页面加载完成之后调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{//这里修改导航栏的标题,动态改变
    self.navigationTitleLab.text = webView.title;
    self.progressView.hidden = YES;
    if (webView.backForwardList.backList.count > 0 && self.closeButton.hidden == YES) {
        //作判断,显示与隐藏关闭按钮
        self.closeButton.hidden = NO;
    }
    
//    //获取网页中的图片
//    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;\
//    };";
//    [webView evaluateJavaScript:jsGetImages completionHandler:nil];
//    [webView evaluateJavaScript:@"getImages()" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
//        NSArray *urlArray = [NSMutableArray arrayWithArray:[result componentsSeparatedByString:@"+"]];
//        //urlResurlt 就是获取到得所有图片的url的拼接;mUrlArray就是所有Url的数组
//        NSLog(@"--%@",[urlArray firstObject]);
//    }];
}
// 页面加载失败时调用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation{
    self.progressView.hidden = YES;
}
// 接收到服务器跳转请求之后再执行
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation{
}
// 在收到响应后,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
    
    WKNavigationResponsePolicy actionPolicy = WKNavigationResponsePolicyAllow;
    //这句是必须加上的,不然会异常
    decisionHandler(actionPolicy);
}
// 在发送请求之前,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
    
    //调起微信等(有些网页点击某个按钮会在微信、QQ或者appStore中打开,在这里监听跳转)
    NSString *url = [navigationAction.request.URL.absoluteString stringByRemovingPercentEncoding];
    if ([url containsString:@"weixin://"] ||
        [url containsString:@"itms-appss://"] ||
        [url containsString:@"mqq://"]) {
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]];
    }
    
    //需要判断targetFrame是否为nil,如果为空则重新请求
    if (navigationAction.targetFrame == nil) {
        [webView loadRequest:navigationAction.request];
    }

    WKNavigationActionPolicy actionPolicy = WKNavigationActionPolicyAllow;
    
    if (navigationAction.navigationType == WKNavigationTypeBackForward) {//判断是返回类型
        //同时设置返回按钮和关闭按钮为导航栏左边的按钮 这里可以监听左滑返回事件,仿微信添加关闭按钮。
        //可以在这里找到指定的历史页面做跳转
    }
    //这句是必须加上的,不然会异常
    decisionHandler(actionPolicy);
}

4、JS和OC的互相调用

设置代理:<WKUIDelegate,WKNavigationDelegate,WKScriptMessageHandler>

4.1、JS调用OC方法

配置环境:

    WKWebViewConfiguration * configuration = [[WKWebViewConfiguration alloc]init];
    userContentController =[[WKUserContentController alloc]init];
    configuration.userContentController = userContentController;
    
    //注册方法
    WKDelegateController * delegateController = [[WKDelegateController alloc]init];
    delegateController.delegate = self;
    [userContentController addScriptMessageHandler:delegateController  name:@"sayhello"];

    webView = [[WKWebView alloc]initWithFrame:self.view.bounds configuration:configuration];
    [self.view addSubview:webView];
    webView.UIDelegate = self;
    webView.navigationDelegate = self;
    [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.test.com"]]];
- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    
    if (_webView) {
         //这里需要注意,前面增加过的方法一定要remove掉。
        [_webView.configuration.userContentController removeScriptMessageHandlerForName:@"sayhello"];
        _webView.UIDelegate = nil;
        _webView.navigationDelegate = nil;
        
        [_webView removeFromSuperview];
        _webView = nil;
    }
}
- (void)dealloc{
}
#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{

// 这里实现原生的方法
if ([message.name isEqualToString:@"sayhello"]) {
        // message.body就是h5传递给原生的参数,这里可以是nil,可以是字符串,可以是数组,可以是字典
    }
}

message.body的类型(h5传值类型):

message.body的类型(h5传值类型)
PS:若参数为空的话,h5调用原生的方法里面一定要传一个‘null’,不然,调不到原生方法。
4.2、OC调用JS方法
- (void)webView:(WKWebView *)tmpWebView didFinishNavigation:(WKNavigation *)navigation{
    //test()是JS方法名,completionHandler是异步回调block
    [webView evaluateJavaScript:@"test()" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
        NSLog(@"%@",result);
    }];
    
}

5、使用过程中遇到的问题

5.1、当用户点击webView上的链接无反应。(在浏览器中点击该网页上的链接,需要打开一个新的页面)

分析:在- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler方法中,参数 WKNavigationAction 中有两个属性:sourceFrame和targetFrame,分别代表这个action的出处和目标。类型是 WKFrameInfo 。WKFrameInfo有一个 mainFrame 的属性,正是这个属性标记着这个frame是在主frame里还是新开一个frame。
如果 targetFrame 的 mainFrame 属性为NO,表明这个 WKNavigationAction 将会新开一个页面。
解决方法:

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

    //需要判断targetFrame是否为nil,如果为空则重新请求
    if (navigationAction.targetFrame == nil) {
        [webView loadRequest:navigationAction.request];
    }

    WKNavigationActionPolicy actionPolicy = WKNavigationActionPolicyAllow;
    //这句是必须加上的,不然会异常
    decisionHandler(actionPolicy);
}
上一篇下一篇

猜你喜欢

热点阅读