IOS瀏覽控件之WKWebView

WkWebView是IOS8中引入的新組件,蘋果將UIWebViewDelegate 與 UIWebView 重構(gòu)成了 14 個(gè)類和 3 個(gè)協(xié)議并引入了不少新的功能和接口。由于一直以來蘋果對(duì)于WebView內(nèi)核封鎖的程度是令人發(fā)指的,WkWebView的引入無疑是另廣大開發(fā)者興奮的事

1、WKWebView介紹

WKWebView是現(xiàn)代 WebKit API 在 iOS 8 和 OS X Yosemite 應(yīng)用中的核心部分。它代替了 UIKit 中的UIWebView和 AppKit 中的WebView,提供了統(tǒng)一的跨雙平臺(tái) API(iOS和OS)。

2、WKWebView新特性

  1. 在性能、穩(wěn)定性、功能方面有很大提升(最直觀的體現(xiàn)就是加載網(wǎng)頁是占用的內(nèi)存,模擬器加載百度與開源中國網(wǎng)站時(shí),WKWebView占用23M,而UIWebView占用85M)
  2. 和 Safari 相同的 JavaScript 引擎,允許JavaScript的Nitro庫加載并使用(UIWebView中限制);
  3. 支持了更多的HTML5特性;
  4. 自詡擁有 60fps 滾動(dòng)刷新率、內(nèi)置手勢(shì)、高效的 app 和 web 信息交換通道

3、WKWebView使用

  • 在WKWebView,一般都是使用UIWebView,而UIWebView的問題主要有:

    • 內(nèi)存占用大
    • 效率低
    • 與JS交互比較麻煩
    • 可擴(kuò)展性低等
  • 使用WKWebView可以有效地解決上述問題。
    示例:

    // 創(chuàng)建webview
    WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
    // 創(chuàng)建請(qǐng)求
    NSURLRequest *request =[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]];
    // 加載網(wǎng)頁
    [webView loadRequest:request];
    // 將webView添加到界面
    [self.view addSubview:webView];
  • WKWebView操作JS
    • WKWebView加載JS

//JS文件路徑
NSString *jsPath = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"js"];
//讀取JS文件內(nèi)容
NSString *jsContent = [NSString stringWithContentsOfFile:jsPath encoding:NSUTF8StringEncoding error:nil];
//創(chuàng)建用戶腳本對(duì)象,
//WKUserScriptInjectionTimeAtDocumentStart :HTML文檔創(chuàng)建后,完成加載前注入,類似于<head>中
//WKUserScriptInjectionTimeAtDocumentEnd :HTML文件完成加載后注入,類似于<body>中
WKUserScript *script = [[WKUserScript alloc] initWithSource:jsContent injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
//添加用戶腳本
[webView.configuration.userContentController addUserScript:script];


- WKWebView執(zhí)行JS方法

//執(zhí)行JS方法
[webView evaluateJavaScript:@"test()" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
//result為執(zhí)行js方法的返回值
if(error){
NSLog(@"Success");
}else{
NSLog(@"Fail");
}
}];


##4、WKWebView代理方法
- WKNavigationDelegate 協(xié)議

pragma mark - WKNavigationDelegate

// 頁面開始加載時(shí)調(diào)用

  • (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
    }
    // 內(nèi)容開始返回時(shí)調(diào)用
  • (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
    }
    // 頁面加載完成時(shí)調(diào)用
  • (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    }
    // 頁面加載失敗時(shí)調(diào)用
  • (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation {
    }
    
      新增的三個(gè)代理方法:
    

// 這個(gè)方法是服務(wù)器重定向時(shí)調(diào)用,即 接收到服務(wù)器跳轉(zhuǎn)請(qǐng)求之后調(diào)用

  • (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
    }
    // 在收到響應(yīng)后,決定是否跳轉(zhuǎn)

  • (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
    }
    // 在發(fā)送請(qǐng)求之前,決定是否跳轉(zhuǎn)

  • (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    //需執(zhí)行decisionHandler的block。
    }

    
    
  • WKUIDelegate 協(xié)議

        #pragma mark - WKUIDelegate
      /// 創(chuàng)建一個(gè)新的WebView
      - (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
          return nil;
      }
    
      /**
       *  web界面中有彈出警告框時(shí)調(diào)用
       *
       *  @param webView           實(shí)現(xiàn)該代理的webview
       *  @param message           警告框中的內(nèi)容
       *  @param frame             主窗口
       *  @param completionHandler 警告框消失調(diào)用
       */
      - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(void (^)())completionHandler {
          
      }
    
      /// 輸入框
      - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler {
          
      }
      /// 確認(rèn)框
      - (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 {
          
      }
    

5、WKWebView疑難

  • WKWebView加載POST請(qǐng)求無法發(fā)送參數(shù)問題

    1. 使用NSURLSession發(fā)送一個(gè)請(qǐng)求,然后把請(qǐng)求下來的數(shù)據(jù)當(dāng)作本地HTML加載
           // 創(chuàng)建WKWebView
        WKWebView *webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
        // 將WKWebView添加到當(dāng)前View
        [self.view addSubview:webView];
        // 設(shè)置訪問的URL
        NSURL *url = [NSURL URLWithString:@"http://www.example.com"];
        // 根據(jù)URL創(chuàng)建請(qǐng)求
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
        // 設(shè)置請(qǐng)求方法為POST
        [request setHTTPMethod:@"POST"];
        // 設(shè)置請(qǐng)求參數(shù)
        [request setHTTPBody:[@"username=aaa&password=123" dataUsingEncoding:NSUTF8StringEncoding]];
        
        // 實(shí)例化網(wǎng)絡(luò)會(huì)話
        NSURLSession *session = [NSURLSession sharedSession];
        // 創(chuàng)建請(qǐng)求Task
        NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            
            // 將請(qǐng)求到的網(wǎng)頁數(shù)據(jù)用loadHTMLString 的方法加載
            NSString *htmlStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            [webView loadHTMLString:htmlStr baseURL:nil];
        }];
        // 開啟網(wǎng)絡(luò)任務(wù)
        [task resume];
    
    1. 使用JavaScript解決WKWebView無法發(fā)送POST參數(shù)問題(iOS8)
  • iOS9以前版本讀取本地HTML的問題
    當(dāng)使用loadRequest來讀取本地的HTML時(shí),WKWebView是無法讀取成功的,后臺(tái)會(huì)出現(xiàn)如下的提示:
    Could not create a sandbox extension for /
    原因是WKWebView是不允許通過loadRequest的方法來加載本地根目錄的HTML文件。
    而在iOS9的SDK中加入了以下方法來加載本地的HTML文件:
    [WKWebView loadFileURL:allowingReadAccessToURL:]
    但是在iOS9以下的版本是沒提供這個(gè)便利的方法的。以下為解決方案的思路,就是在iOS9以下版本時(shí),先將本地HTML文件的數(shù)據(jù)copy到tmp目錄中,然后再使用loadRequest來加載。但是如果在HTML中加入了其他資源文件,例如js,css,image等必須一同copy到temp中。這個(gè)是最蛋疼的事情了。

        //將文件copy到tmp目錄
      - (NSURL *)fileURLForBuggyWKWebView8:(NSURL *)fileURL {
          NSError *error = nil;
          if (!fileURL.fileURL || ![fileURL checkResourceIsReachableAndReturnError:&error]) {
              return nil;
          }
          // Create "/temp/www" directory
          NSFileManager *fileManager= [NSFileManager defaultManager];
          NSURL *temDirURL = [[NSURL fileURLWithPath:NSTemporaryDirectory()] URLByAppendingPathComponent:@"www"];
          [fileManager createDirectoryAtURL:temDirURL withIntermediateDirectories:YES attributes:nil error:&error];
          
          NSURL *dstURL = [temDirURL URLByAppendingPathComponent:fileURL.lastPathComponent];
          // Now copy given file to the temp directory
          [fileManager removeItemAtURL:dstURL error:&error];
          [fileManager copyItemAtURL:fileURL toURL:dstURL error:&error];
          // Files in "/temp/www" load flawlesly :)
          return dstURL;
      }
    
      //調(diào)用邏輯
      NSString *path = [[NSBundle mainBundle] pathForResource:@"indexoff" ofType:@"html"];
      if(path){
          if ([[UIDevice currentDevice].systemVersion floatValue] >= 9.0) {
              // iOS9. One year later things are OK.
              NSURL *fileURL = [NSURL fileURLWithPath:path];
              [self.webView loadFileURL:fileURL allowingReadAccessToURL:fileURL];
          } else {
              // iOS8. Things can be workaround-ed
              //   Brave people can do just this
              //   fileURL = try! pathForBuggyWKWebView8(fileURL)
              //   webView.loadRequest(NSURLRequest(URL: fileURL))
              
              NSURL *fileURL = [self.fileHelper fileURLForBuggyWKWebView8:[NSURL fileURLWithPath:path]];
              NSURLRequest *request = [NSURLRequest requestWithURL:fileURL];
              [self.webView loadRequest:request];
          }
      }
    

問題:http://stackoverflow.com/questions/24882834/wkwebview-not-loading-local-files-under-ios-8
具體參見:https://github.com/shazron/WKWebViewFIleUrlTest

ps:在實(shí)際測(cè)試中,上述方法在iOS8.0系統(tǒng)中也無法成功。如果在iOS8.0系統(tǒng)有成功的請(qǐng)告知下。謝謝!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容