WKWebView詳解

WKWebView是在Apple的WWDC 2014隨iOS 8和OS X 10.10出來的,是爲了解決UIWebView加載速度慢、佔用內存大的問題。
使用UIWebView加載網頁的時候,咱們會發現內存會無限增加,還有內存泄漏的問題存在。
WebKit中更新的WKWebView控件的新特性與使用方法,它很好的解決了UIWebView存在的內存、加載速度等諸多問題。java

1、WKWebView新特性

在性能、穩定性、功能方面有很大提高(最直觀的體現就是加載網頁是佔用的內存);
容許JavaScript的Nitro庫加載並使用(UIWebView中限制);
支持了更多的HTML5特性;
高達60fps的滾動刷新率以及內置手勢;
將UIWebViewDelegate與UIWebView重構成了14類與3個協議查看蘋果官方文檔ios

2、WebKit框架概覽

如上圖所示,WebKit框架中最核心的類應該屬於WKWebView了,這個類專門用來渲染網頁視圖,其餘類和協議都將基於它和服務於它。
WKWebView:網頁的渲染與展現,經過WKWebViewConfiguration能夠進行自定義配置。
WKWebViewConfiguration:這個類專門用來配置WKWebView。
WKPreference:這個類用來進行相關webView設置。
WKProcessPool:這個類用來配置進程池,與網頁視圖的資源共享有關。
WKUserContentController:這個類主要用來作native與JavaScript的交互管理。
WKUserScript:用於進行JavaScript注入。
WKScriptMessageHandler:這個類專門用來處理JavaScript調用native的方法。
WKNavigationDelegate:網頁跳轉間的導航管理協議,這個協議能夠監聽網頁的活動。
WKNavigationAction:網頁某個活動的示例化對象。
WKUIDelegate:用於交互處理JavaScript中的一些彈出框。
WKBackForwardList:堆棧管理的網頁列表。
WKBackForwardListItem:每一個網頁節點對象。web

3、WKWebView的屬性

/// webView的自定義配置
@property (nonatomic, readonly, copy) WKWebViewConfiguration *configuration;

/// 導航代理
@property (nullable, nonatomic, weak) id <WKNavigationDelegate> navigationDelegate;

/// UI代理
@property (nullable, nonatomic, weak) id <WKUIDelegate> UIDelegate;

/// 訪問過網頁歷史列表
@property (nonatomic, readonly, strong) WKBackForwardList *backForwardList;

/// 自定義初始化webView
- (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration NS_DESIGNATED_INITIALIZER;

- (nullable instancetype)initWithCoder:(NSCoder *)coder NS_DESIGNATED_INITIALIZER;

/// url加載webView視圖
- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;

/// 文件加載webView視圖
- (nullable WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL API_AVAILABLE(macosx(10.11), ios(9.0));

/// HTMLString字符串加載webView視圖
- (nullable WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;

/// NSData數據加載webView視圖
- (nullable WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL API_AVAILABLE(macosx(10.11), ios(9.0));

/// 返回上一個網頁節點
- (nullable WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item;

/// 網頁的標題
@property (nullable, nonatomic, readonly, copy) NSString *title;

/// 網頁的URL地址
@property (nullable, nonatomic, readonly, copy) NSURL *URL;

/// 網頁是否正在加載
@property (nonatomic, readonly, getter=isLoading) BOOL loading;

/// 加載的進度 範圍爲[0, 1]
@property (nonatomic, readonly) double estimatedProgress;

/// 網頁連接是否安全
@property (nonatomic, readonly) BOOL hasOnlySecureContent;

/// 證書服務
@property (nonatomic, readonly, nullable) SecTrustRef serverTrust API_AVAILABLE(macosx(10.12), ios(10.0));

/// 是否能夠返回
@property (nonatomic, readonly) BOOL canGoBack;

/// 是否能夠前進
@property (nonatomic, readonly) BOOL canGoForward;

/// 返回到上一個網頁
- (nullable WKNavigation *)goBack;

/// 前進到下一個網頁
- (nullable WKNavigation *)goForward;

/// 從新加載
- (nullable WKNavigation *)reload;

/// 忽略緩存 從新加載
- (nullable WKNavigation *)reloadFromOrigin;

/// 中止加載
- (void)stopLoading;

/// 執行JavaScript
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;

/// 是否容許左右滑動,返回-前進操做  默認是NO
@property (nonatomic) BOOL allowsBackForwardNavigationGestures;

/// 自定義代理字符串
@property (nullable, nonatomic, copy) NSString *customUserAgent API_AVAILABLE(macosx(10.11), ios(9.0));

/// 在iOS上默認爲NO,標識不容許連接預覽
@property (nonatomic) BOOL allowsLinkPreview API_AVAILABLE(macosx(10.11), ios(9.0));

/// 滾動視圖
@property (nonatomic, readonly, strong) UIScrollView *scrollView;

/// 是否支持放大手勢,默認爲NO
@property (nonatomic) BOOL allowsMagnification;

/// 放大因子,默認爲1
@property (nonatomic) CGFloat magnification;

/// 據設置的縮放因子來縮放頁面,並居中顯示結果在指定的點
- (void)setMagnification:(CGFloat)magnification centeredAtPoint:(CGPoint)point;

/// 證書列表
@property (nonatomic, readonly, copy) NSArray *certificateChain API_DEPRECATED_WITH_REPLACEMENT("serverTrust", macosx(10.11, 10.12), ios(9.0, 10.0));

4、WKWebView的使用

簡單使用,直接加載url地址macos

WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://developer.apple.com/reference/webkit"]]];    
[self.view addSubview:webView];

自定義配置
再WKWebView裏面註冊供JS調用的方法,是經過WKUserContentController類下面的方法:緩存

- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;安全

// 建立配置
    WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
    // 建立UserContentController(提供JavaScript向webView發送消息的方法)
    WKUserContentController* userContent = [[WKUserContentController alloc] init];
    // 添加消息處理,注意:self指代的對象須要遵照WKScriptMessageHandler協議,結束時須要移除
    [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:@"https://developer.apple.com/reference/webkit"];

    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [webView loadRequest:request];
    [self.view addSubview:webView];

實現WKScriptMessageHandler協議方法服務器

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
  // 判斷是不是調用原生的
   if ([@"NativeMethod" isEqualToString:message.name]) {
     // 判斷message的內容,而後作相應的操做
      if ([@"close" isEqualToString:message.body]) {

      }
   }
}

注意:上面將當前ViewController設置爲MessageHandler以後須要在當前ViewController銷燬前將其移除,不然會形成內存泄漏。app

[self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"NativeMethod"];

5、WKNavigationDelegate代理方法

若是實現了代理方法,必定要在decidePolicyForNavigationAction和decidePolicyForNavigationResponse方法中的回調設置容許跳轉。框架

typedef NS_ENUM(NSInteger, WKNavigationActionPolicy) {
WKNavigationActionPolicyCancel, // 取消跳轉
WKNavigationActionPolicyAllow, // 容許跳轉
} API_AVAILABLE(macosx(10.10), ios(8.0));ide

// 1 在發送請求以前,決定是否跳轉
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    NSLog(@"1-------在發送請求以前,決定是否跳轉  -->%@",navigationAction.request);

    decisionHandler(WKNavigationActionPolicyAllow);
}

// 2 頁面開始加載時調用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
    NSLog(@"2-------頁面開始加載時調用");
}

// 3 在收到響應後,決定是否跳轉
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
    /// 在收到服務器的響應頭,根據response相關信息,決定是否跳轉。decisionHandler必須調用,來決定是否跳轉,參數WKNavigationActionPolicyCancel取消跳轉,WKNavigationActionPolicyAllow容許跳轉

    NSLog(@"3-------在收到響應後,決定是否跳轉");

    decisionHandler(WKNavigationResponsePolicyAllow);
}

// 4 當內容開始返回時調用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
    NSLog(@"4-------當內容開始返回時調用");
}

// 5 頁面加載完成以後調用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    NSLog(@"5-------頁面加載完成以後調用");
}

// 6 頁面加載失敗時調用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation {

    NSLog(@"6-------頁面加載失敗時調用");
}

// 接收到服務器跳轉請求以後調用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
    NSLog(@"-------接收到服務器跳轉請求以後調用");
}

// 數據加載發生錯誤時調用
- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
    NSLog(@"----數據加載發生錯誤時調用");
}

// 須要響應身份驗證時調用 一樣在block中須要傳入用戶身份憑證
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
    //用戶身份信息

    NSLog(@"----須要響應身份驗證時調用 一樣在block中須要傳入用戶身份憑證");

    NSURLCredential *newCred = [NSURLCredential credentialWithUser:@""
                                                          password:@""
                                                       persistence:NSURLCredentialPersistenceNone];
    // 爲 challenge 的發送方提供 credential
    [[challenge sender] useCredential:newCred forAuthenticationChallenge:challenge];
    completionHandler(NSURLSessionAuthChallengeUseCredential,newCred);
}

// 進程被終止時調用
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView {
    NSLog(@"----------進程被終止時調用");
}

6、WKUIDelegate代理方法

/**
 *  web界面中有彈出警告框時調用
 *
 *  @param webView           實現該代理的webview
 *  @param message           警告框中的內容
 *  @param completionHandler 警告框消失調用
 */
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(void (^)())completionHandler {

    NSLog(@"-------web界面中有彈出警告框時調用");
}

// 建立新的webView時調用的方法
- (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {

    NSLog(@"-----建立新的webView時調用的方法");
    return webView;
}

// 關閉webView時調用的方法
- (void)webViewDidClose:(WKWebView *)webView {

    NSLog(@"----關閉webView時調用的方法");
}

// 下面這些方法是交互JavaScript的方法
// JavaScript調用confirm方法後回調的方法 confirm是js中的肯定框,須要在block中把用戶選擇的狀況傳遞進去
-(void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler {

    NSLog(@"%@",message);
    completionHandler(YES);
}
// JavaScript調用prompt方法後回調的方法 prompt是js中的輸入框 須要在block中把用戶輸入的信息傳入
-(void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler{

    NSLog(@"%@",prompt);
    completionHandler(@"123");
}

// 默認預覽元素調用
- (BOOL)webView:(WKWebView *)webView shouldPreviewElement:(WKPreviewElementInfo *)elementInfo {

    NSLog(@"-----默認預覽元素調用");
    return YES;
}

// 返回一個視圖控制器將致使視圖控制器被顯示爲一個預覽。返回nil將WebKit的默認預覽的行爲。
- (nullable UIViewController *)webView:(WKWebView *)webView previewingViewControllerForElement:(WKPreviewElementInfo *)elementInfo defaultActions:(NSArray<id <WKPreviewActionItem>> *)previewActions {

    NSLog(@"----返回一個視圖控制器將致使視圖控制器被顯示爲一個預覽。返回nil將WebKit的默認預覽的行爲。");
    return self;
}

// 容許應用程序向它建立的視圖控制器彈出
- (void)webView:(WKWebView *)webView commitPreviewingViewController:(UIViewController *)previewingViewController {

    NSLog(@"----容許應用程序向它建立的視圖控制器彈出");

}

// 顯示一個文件上傳面板。completionhandler完成處理程序調用後打開面板已被撤銷。經過選擇的網址,若是用戶選擇肯定,不然爲零。若是不實現此方法,Web視圖將表現爲若是用戶選擇了取消按鈕。
- (void)webView:(WKWebView *)webView runOpenPanelWithParameters:(WKOpenPanelParameters *)parameters initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSArray<NSURL *> * _Nullable URLs))completionHandler {

    NSLog(@"----顯示一個文件上傳面板");

}
相關文章
相關標籤/搜索