天天看点

iOS 学习 --- WKWebView使用

WKWebView是苹果在iOS 8之后推出的框架WebKit中的浏览器控件,其加载速度比UIWebView快了许多, 但内存占用率却下降很多,也解决了加载网页时的内存泄露问题。现在的项目大多数只需适配到iOS 8, 所以用WKWebView来替换项目中的UIWebView是很有必要的。

WKWebView常用到的几个类:

  • WKWebView
  • WKWebViewConfiguration
  • WKUserScript
  • WKUserContentController
  • WKWebsiteDataStore:清理缓存

以及3个代理:

  • WKUIDelegate
  • WKNavigationDelegate
  • WKScriptMessageHandler

WKWebView常用属性

WKUIDelegate 代理方法

这个代理方法, 主要是用来处理使用系统的弹框来替换JS中的一些弹框的,比如: 警告框, 选择框, 输入框, 主要使用的是下面三个代理方法:
// 创建一个新的WebView(标签带有 target='_blank' 时,导致WKWebView无法加载点击后的网页的问题。)
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
    // 接口的作用是打开新窗口委托
    WKFrameInfo *frameInfo = navigationAction.targetFrame;
    if (![frameInfo isMainFrame]) {
        [webView loadRequest:navigationAction.request];
    }
    return nil;
}
           
/**
 警告框
 在JS端调用alert函数时,会触发此代理方法。
 JS端调用alert时所传的数据可以通过message拿到
 在原生得到结果后,需要回调JS,是通过completionHandler回调
*/
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
    NSLog(@"%s", __FUNCTION__);
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"alert" message:@"JS调用alert" preferredStyle:UIAlertControllerStyleAlert];
    [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler();
    }]];

    [self presentViewController:alert animated:YES completion:NULL];
    NSLog(@"%@", message);
}
           
/**
 确认框
 JS端调用confirm函数时,会触发此方法
 通过message可以拿到JS端所传的数据
 在iOS端显示原生alert得到YES/NO后
 通过completionHandler回调给JS端
*/
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler {
    NSLog(@"%s", __FUNCTION__);

    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"confirm" message:@"JS调用confirm" preferredStyle:UIAlertControllerStyleAlert];
    [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(YES);
    }]];
    [alert addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(NO);
    }]];
    [self presentViewController:alert animated:YES completion:NULL];

    NSLog(@"%@", message);
}
           
/**
 输入框
 JS端调用prompt函数时,会触发此方法
 要求输入一段文本
 在原生输入得到文本内容后,通过completionHandler回调给JS
*/

- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler {
    NSLog(@"%s", __FUNCTION__);

    NSLog(@"%@", prompt);
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"textinput" message:@"JS调用输入框" preferredStyle:UIAlertControllerStyleAlert];
    [alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
        textField.textColor = [UIColor redColor];
    }];

    [alert addAction:[UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler([[alert.textFields lastObject] text]);
    }]];

    [self presentViewController:alert animated:YES completion:NULL];
}
           

WKNavigationDelegate代理方法

WKNavigationDelegate:最常用,和UIWebViewDelegate功能类似,该代理提供的方法,可以用来追踪加载过程(页面开始加载、加载完成、加载失败)、决定是否执行跳转。下面会对函数做简单的说明,并用数字标出调用的先后次序:11-22-33-44-55。

用来追踪加载过程(页面开始加载、加载完成、加载失败)的方法: 

/**
 *  22,页面开始加载时调用
 *
 *  @param webView    实现该代理的webview
 *  @param navigation 当前navigation
 */
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
    NSLog(@"%s", __FUNCTION__);
}
           
/**
 *  44,当内容开始返回时调用
 *
 *  @param webView    实现该代理的webview
 *  @param navigation 当前navigation
 */
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {
    NSLog(@"%s", __FUNCTION__);
}
           
/**
 *  55,页面加载完成之后调用
 *
 *  @param webView    实现该代理的webview
 *  @param navigation 当前navigation
 */
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {
    NSLog(@"%s", __FUNCTION__);
}
           
/**
 *  4,加载失败时调用
 *
 *  @param webView    实现该代理的webview
 *  @param navigation 当前navigation
 *  @param error      错误
 */
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
    NSLog(@"%s", __FUNCTION__);
}
           

页面跳转的代理方法: 

/**
 *  1,接收到服务器跳转请求之后调用
 *
 *  @param webView      实现该代理的webview
 *  @param navigation   当前navigation
 */
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
    NSLog(@"%s", __FUNCTION__);
}
           
/**
 *  11,在发送请求之前,决定是否跳转
 *
 *  @param webView          实现该代理的
 *  @param navigationAction 当前navigation
 *  @param decisionHandler  是否调转block
 */
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    NSString *hostname = navigationAction.request.URL.host.lowercaseString;
    if (navigationAction.navigationType == WKNavigationTypeLinkActivated
        && ![hostname containsString:@".baidu.com"]) {
        // 对于跨域,需要手动跳转
        [[UIApplication sharedApplication] openURL:navigationAction.request.URL];

        // 不允许web内跳转
        decisionHandler(WKNavigationActionPolicyCancel);
    } else {
       self.progressView.alpha = 1.0;
        decisionHandler(WKNavigationActionPolicyAllow);
    }

    NSLog(@"%s", __FUNCTION__);
}
           
/**
 *  33,在收到响应后,决定是否跳转。在收到服务器的响应头,根据response相关信息,决定是否跳转。decisionHandler必须调用,来决定是否跳转,参数WKNavigationActionPolicyCancel取消跳转,WKNavigationActionPolicyAllow允许跳转。
 *
 *  @param webView            实现该代理的webview
 *  @param navigationResponse 当前navigation
 *  @param decisionHandler    是否跳转block
 */
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {

    //    NSLog(@"%@",navigationResponse.response);

    // 如果响应的地址是百度,则允许跳转
    //    if ([navigationResponse.response.URL.host.lowercaseString isEqual:@"www.baidu.com"]) {
    //
    //        // 允许跳转
    //        decisionHandler(WKNavigationResponsePolicyAllow);
    //        return;
    //    }
    //    // 不允许跳转
    //    decisionHandler(WKNavigationResponsePolicyCancel);
    decisionHandler(WKNavigationResponsePolicyAllow);
}
           

其他一些方法

// 当main frame最后下载数据失败时,会回调
- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
}

// 用于授权验证的API,与AFN、UIWebView的授权验证API是一样的
// 对于HTTPS的都会触发此代理,如果不要求验证,传默认就行
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *__nullable credential))completionHandler{
    
    completionHandler(NSURLSessionAuthChallengePerformDefaultHandling ,nil);
}

/**
9.0才能使用,web内容处理中断时会触发
当web content处理完成时,会回调
在 UIWebView 上当内存占用太大的时候,App Process 会 crash,在 WKWebView 上当总体的内存占用比较大的时候,WebContent Process 会 crash,从而出现白屏现象。
iOS 9以后 WKNavigtionDelegate 新增了一个回调函数:

*/
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView {
    [webView reload];
    NSLog(@"进程终止");
}
           

WKWebView 执行JS代码

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSString *jsString = [NSString stringWithFormat:@"showSatisfaction()"];
        
        [self.wkWebView evaluateJavaScript:jsString completionHandler:^(id response, NSError * error) {
            NSLog(@"error: %@", error.description);
            NSLog(@"response: %@", response);
            //js返回0:js控制跳出弹框 1:返回上级页面
            if ([response intValue] == 1) {
                   [self.navigationController popViewControllerAnimated:YES];
               }
        }];
    });
           

WKScriptMessageHandler代理方法

// 从web界面中接收到一个脚本时调用

 -(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
    
    //JS调用OC方法
    //message.boby就是JS里传过来的参数
    NSLog(@"body:%@",message.body);
    
    if ([message.name isEqualToString:@"Share"]) {
        
            [self ShareWithInformation:message.body];
    
    } else if ([message.name isEqualToString:@"Camera"]) {
        
            [self camera];
    }
    
}
           
 WKScriptMessage有两个关键属性name 和 body。因为我们给每一个OC方法取了一个name,所以就可以根据name 来区分执行不同的方法。body 中存着JS 要给OC 传的参数。
-(void)ShareWithInformation:(NSDictionary *)dic{
    
    if (![dic isKindOfClass:[NSDictionary class]]) {
            return;
    }
    
    NSString *title = [dic objectForKey:@"title"];
    NSString *content = [dic objectForKey:@"content"];
    NSString *url = [dic objectForKey:@"url"];
    //在这里写分享操作的代码
    NSLog(@"要分享了哦?");
    //OC反馈给JS分享结果
    NSString *JSResult = [NSString stringWithFormat:@"shareResult('%@','%@','%@')",title,content,url];
    //OC调用JS
    [self.webView evaluateJavaScript:JSResult completionHandler:^(id _Nullable result, NSError * _Nullable error) {
        
            NSLog(@"%@", error);
    }];
}
           

相关文章

[iOS]WKWebView的使用--API篇

UIWebView、WKWebView使用详解及性能分析

iOS WKWebView的基本使用

iOS WKWebView(清理缓存)

iOS OC与JS的交互(WKWebview-MessageHandler实现)