WKWebView是iOS 8推出,输入WebKit.framework, UIWebView属于UIKit.framework; WKWebView相对UIWebView优化了很多,特别是内存的消耗.
经测试通过UIWebView和WKWebView分别访问http:www.baidu.com,App正常启动后内存平稳在42M,点击按钮使用UIWebView加载baidu内存最高峰达到131M,然后平稳在119M;
通过WKWebView记载baidu内存最高峰达到47.2M,然后平稳在46.6M.

WKWebView特性:

  • 在性能、稳定性、功能方面有很大提升.
  • 允许JavaScript的Nitro库加载并使用(UIWebView中限制).
  • 支持了更多的HTML5特性.
  • 高达60fps的滚动刷新率以及内置手势
  • 将UIWebViewDelegate与UIWebView重构成了14类与3个协议
  • 基本使用:

  • 加载的状态回调
  • 新的 WKUIDelegate 协议
  • 动态加载并运行 JS 代码
  • webView 执行 JS 代码
  • JS 调用App注册过的方法
  • - (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;
    - (nullable WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL API_AVAILABLE(macosx(10.11), ios(9.0));
    - (nullable WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
    - (nullable WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL API_AVAILABLE(macosx(10.11), ios(9.0));

    其中有2个方法是iOS 9才有的

    注意: load方法中使用的baseURL参数,此参数是html基于的路径,比如html中使用的资源等路径,

    [self loadHTMLString:string baseURL:[[NSBundle mainBundle] bundleURL]];

    网页中的资源图片等可以写相对路径,相对bundelURL下的资源文件.

    NSURL *url = [NSURL URLWithString:@"http://www.baidu.com"]; // [NSURL fileURLWithPath:path]
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [self.webView loadRequest:request];

    加载状态回调

    追踪加载过程代理方法:

    // 页面开始加载时调用
    - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation{
    // 页面加载失败时调用
    - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error{
    // 当内容开始返回时调用
    - (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation{
    // 页面加载完成之后调用
    - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation{
    // 加载失败
    - (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error{
    

    页面跳转代理方法:

    // 在发送请求之前,决定是否跳转
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
        decisionHandler(WKNavigationActionPolicyAllow);
    // 在收到响应后,决定是否跳转
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
        decisionHandler(WKNavigationResponsePolicyAllow);
    // 接收到服务器跳转请求之后调用
    - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation{
    // 接受认证
    - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler{
        if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
            NSURLCredential *card = [[NSURLCredential alloc]initWithTrust:challenge.protectionSpace.serverTrust];
            completionHandler(NSURLSessionAuthChallengeUseCredential,card);
    

    新的WKUIDelegate协议

    这个协议主要用于WKWebView处理web界面的三种提示框(警告框、确认框、输入框)

    // 窗口已经关闭
    - (void)webViewDidClose:(WKWebView *)webView API_AVAILABLE(macosx(10.11), ios(9.0)){
     alert框
     @param webView WKWebView
     @param message 消息文本内容
     @param frame frame主窗口
     @param completionHandler 窗口消失回调
    - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{
    // 确认框
    - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler{
    // 文本输入框
    - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler{
    

    动态加载并运行JS代码

    // 图片缩放的js代码
    NSString *js = @"var count = document.images.length;for (var i = 0; i < count; i++) {var image = document.images[i];image.style.width=320;};window.alert('找到' + count + '张图');";
    // 根据JS字符串初始化WKUserScript对象
    WKUserScript *script = [[WKUserScript alloc] initWithSource:js injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
    // 根据生成的WKUserScript对象,初始化WKWebViewConfiguration
    WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
    [config.userContentController addUserScript:script];
    _webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config];
    [_webView loadHTMLString:@"<head></head><imgea src='http://www.nsu.edu.cn/v/2014v3/img/background/3.jpg' />"baseURL:nil];
    [self.view addSubview:_webView];

    webView 执行JS代码

    //javaScriptString是JS方法名,completionHandler是异步回调block
    [self.webView evaluateJavaScript:javaScriptString completionHandler:completionHandler];

    JS调用App注册过的方法

    WKWebView里面注册供JS调用的方法,是通过WKUserContentController类下面的方法:

    - (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;
    [self.webView.configuration.userContentController addScriptMessageHandler:self name:@"openYou"];

    scriptMessageHandler是代理回调; JS调用name方法后,OC会调用scriptMessageHandler指定的对象

    实现代理方法

    - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
        NSLog(@"%s",__func__);
    

    页面点击事件

    function btnClick(){
        //JS调用
        window.webkit.messageHandlers.openYou.postMessage(null);
    

    name(方法名)是放在中间的,messageBody只能是一个对象,如果要传多个值,需要封装成数组,或者字典.

    当js方法btnClick调用时会触发OC的代理方法

    - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;

    WKWebView拦截URL请求

    // 在发送请求之前,决定是否跳转
    - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
    //    navigationAction.request.URL.absoluteString 根据请求的Url进行拦截请求处理
        decisionHandler(WKNavigationActionPolicyAllow);
    

    Alert弹窗解决方案

    alert框 @param webView WKWebView @param message 消息文本内容 @param frame frame主窗口 @param completionHandler 窗口消失回调 - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{ kFunLog 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 result))completionHandler{ kFunLog 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:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler{ kFunLog 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];

    禁止选择操作

    // 禁止选择CSS
            NSString *css = @"body{-webkit-user-select:none;}";
            // CSS选中样式取消 此方式可能会影响dataDetectorTypes属性
            NSMutableString *javascript = [NSMutableString string];
            [javascript appendString:@"var style = document.createElement('style');"];
            [javascript appendString:@"style.type = 'text/css';"];
            [javascript appendFormat:@"var cssContent = document.createTextNode('%@');", css];
            [javascript appendString:@"style.appendChild(cssContent);"];
            [javascript appendString:@"document.body.appendChild(style);"];
            // javascript注入
            WKUserScript *noneSelectScript = [[WKUserScript alloc] initWithSource:javascript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
            WKUserContentController *userContentController = [[WKUserContentController alloc] init];
            [userContentController addUserScript:noneSelectScript];
            WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
            configuration.userContentController = userContentController;
    WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];if(greaterIOS10){
          configuration.dataDetectorTypes = WKDataDetectorTypePhoneNumber | WKDataDetectorTypeLink | WKDataDetectorTypeCalendarEvent;
    
    - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation{
        if([webView isLoading]){
            return;
        // 禁用用户菜单操作效果
        [webView evaluateJavaScript:@"document.documentElement.style.webkitUserSelect='none'" completionHandler:nil];
        [webView evaluateJavaScript:@"document.documentElement.style.webkitTouchCallout='none'" completionHandler:nil];
    

    此方式1采用JavaScript注入CSS方式实现的过程经测试调用会影响页面电话号码,超链接等的识别. 本人调试过,去掉css注入就可以识别, 最后采用方式2解决禁止选中效果 

    WKWebView滚动渲染时肯能出现空白问题解决方式

    - (void)scrollViewDidScroll:(UIScrollView *)scrollView{
        // 渲染WKWebView ((void(*)(id,SEL,BOOL))objc_msgSend)(self.webView,@selector(_updateVisibleContentRects),NO);
    //    objc_msgSend(person, @selector(run));
        if ([self.twebView respondsToSelector:@selector(_updateVisibleContentRects)]) {
            ((void(*)(id,SEL,BOOL))objc_msgSend)(self.twebView,@selector(_updateVisibleContentRects),NO);
    

    .WebView内容字体大小

    根据app字体大小的调整,webView的字体大小调整

    - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation{
        if([webView isLoading]){
            return;
        // 禁用用户菜单操作效果
        [webView evaluateJavaScript:@"document.documentElement.style.webkitUserSelect='none'" completionHandler:nil];
        [webView evaluateJavaScript:@"document.documentElement.style.webkitTouchCallout='none'" completionHandler:nil];
        // 控制字体大小
        // (initial-scale是初始缩放比,minimum-scale=1.0最小缩放比,maximum-scale=5.0最大缩放比,user-scalable=yes是否支持缩放)
        NSString *metaJS = [NSString stringWithFormat:@"document.getElementsByName(\"viewport\")[0].content = \"width=self.view.frame.size.width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=yes\""];
        [webView evaluateJavaScript:metaJS  completionHandler:nil];
        CGFloat fontSize = [AppFontSizeUtil getMailContentFontProportionOfNormal];
        // 修改百分比即可 @"document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust= '150%'"
        NSString *fontSizeJs = [NSString stringWithFormat:@"document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust= '%f%%'",fontSize*100];
        [webView evaluateJavaScript:fontSizeJs  completionHandler:nil];