WKWebView詳解

WKWebView是在Apple的WWDC 2014隨iOS 8和OS X 10.10出來的,是為了解決UIWebView加載速度慢、占用內(nèi)存大的問題。
使用UIWebView加載網(wǎng)頁的時候,我們會發(fā)現(xiàn)內(nèi)存會無限增長,還有內(nèi)存泄漏的問題存在。
WebKit中更新的WKWebView控件的新特性與使用方法,它很好的解決了UIWebView存在的內(nèi)存、加載速度等諸多問題。

一、WKWebView新特性

在性能、穩(wěn)定性、功能方面有很大提升(最直觀的體現(xiàn)就是加載網(wǎng)頁是占用的內(nèi)存);
允許JavaScript的Nitro庫加載并使用(UIWebView中限制);
支持了更多的HTML5特性;
高達(dá)60fps的滾動刷新率以及內(nèi)置手勢;
將UIWebViewDelegate與UIWebView重構(gòu)成了14類與3個協(xié)議查看蘋果官方文檔

二、WebKit框架概覽

如上圖所示,WebKit框架中最核心的類應(yīng)該屬于WKWebView了,這個類專門用來渲染網(wǎng)頁視圖,其他類和協(xié)議都將基于它和服務(wù)于它。
WKWebView:網(wǎng)頁的渲染與展示,通過WKWebViewConfiguration可以進(jìn)行自定義配置。
WKWebViewConfiguration:這個類專門用來配置WKWebView。
WKPreference:這個類用來進(jìn)行相關(guān)webView設(shè)置。
WKProcessPool:這個類用來配置進(jìn)程池,與網(wǎng)頁視圖的資源共享有關(guān)。
WKUserContentController:這個類主要用來做native與JavaScript的交互管理。
WKUserScript:用于進(jìn)行JavaScript注入。
WKScriptMessageHandler:這個類專門用來處理JavaScript調(diào)用native的方法。
WKNavigationDelegate:網(wǎng)頁跳轉(zhuǎn)間的導(dǎo)航管理協(xié)議,這個協(xié)議可以監(jiān)聽網(wǎng)頁的活動。
WKNavigationAction:網(wǎng)頁某個活動的示例化對象。
WKUIDelegate:用于交互處理JavaScript中的一些彈出框。
WKBackForwardList:堆棧管理的網(wǎng)頁列表。
WKBackForwardListItem:每個網(wǎng)頁節(jié)點(diǎn)對象。

三、WKWebView的屬性

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

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

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

/// 訪問過網(wǎng)頁歷史列表
@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數(shù)據(jù)加載webView視圖
- (nullable WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL API_AVAILABLE(macosx(10.11), ios(9.0));

/// 返回上一個網(wǎng)頁節(jié)點(diǎn)
- (nullable WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item;

/// 網(wǎng)頁的標(biāo)題
@property (nullable, nonatomic, readonly, copy) NSString *title;

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

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

/// 加載的進(jìn)度 范圍為[0, 1]
@property (nonatomic, readonly) double estimatedProgress;

/// 網(wǎng)頁鏈接是否安全
@property (nonatomic, readonly) BOOL hasOnlySecureContent;

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

/// 是否可以返回
@property (nonatomic, readonly) BOOL canGoBack;

/// 是否可以前進(jìn)
@property (nonatomic, readonly) BOOL canGoForward;

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

/// 前進(jìn)到下一個網(wǎng)頁
- (nullable WKNavigation *)goForward;

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

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

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

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

/// 是否允許左右滑動,返回-前進(jìn)操作  默認(rèn)是NO
@property (nonatomic) BOOL allowsBackForwardNavigationGestures;

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

/// 在iOS上默認(rèn)為NO,標(biāo)識不允許鏈接預(yù)覽
@property (nonatomic) BOOL allowsLinkPreview API_AVAILABLE(macosx(10.11), ios(9.0));

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

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

/// 放大因子,默認(rèn)為1
@property (nonatomic) CGFloat magnification;

/// 據(jù)設(shè)置的縮放因子來縮放頁面,并居中顯示結(jié)果在指定的點(diǎn)
- (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));

四、WKWebView的使用

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

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調(diào)用的方法,是通過WKUserContentController類下面的方法:
- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;

    // 創(chuàng)建配置
    WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
    // 創(chuàng)建UserContentController(提供JavaScript向webView發(fā)送消息的方法)
    WKUserContentController* userContent = [[WKUserContentController alloc] init];
    // 添加消息處理,注意:self指代的對象需要遵守WKScriptMessageHandler協(xié)議,結(jié)束時需要移除
    [userContent addScriptMessageHandler:self name:@"NativeMethod"];
    // 將UserConttentController設(shè)置到配置文件
    config.userContentController = userContent;
    // 高端的自定義配置創(chuàng)建WKWebView
    WKWebView *webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds configuration:config];
    // 設(shè)置訪問的URL
    NSURL *url = [NSURL URLWithString:@"https://developer.apple.com/reference/webkit"];
    
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [webView loadRequest:request];
    [self.view addSubview:webView];

實(shí)現(xiàn)WKScriptMessageHandler協(xié)議方法

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    // 判斷是否是調(diào)用原生的
    if ([@"NativeMethod" isEqualToString:message.name]) {
        // 判斷message的內(nèi)容,然后做相應(yīng)的操作
        if ([@"close" isEqualToString:message.body]) {
            
        }
    }
}

注意:上面將當(dāng)前ViewController設(shè)置為MessageHandler之后需要在當(dāng)前ViewController銷毀前將其移除,否則會造成內(nèi)存泄漏。

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

五、WKNavigationDelegate代理方法

如果實(shí)現(xiàn)了代理方法,一定要在decidePolicyForNavigationAction和decidePolicyForNavigationResponse方法中的回調(diào)設(shè)置允許跳轉(zhuǎn)。

typedef NS_ENUM(NSInteger, WKNavigationActionPolicy) {
WKNavigationActionPolicyCancel, // 取消跳轉(zhuǎn)
WKNavigationActionPolicyAllow, // 允許跳轉(zhuǎn)
} API_AVAILABLE(macosx(10.10), ios(8.0));

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

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

// 3 在收到響應(yīng)后,決定是否跳轉(zhuǎn)
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
    /// 在收到服務(wù)器的響應(yīng)頭,根據(jù)response相關(guān)信息,決定是否跳轉(zhuǎn)。decisionHandler必須調(diào)用,來決定是否跳轉(zhuǎn),參數(shù)WKNavigationActionPolicyCancel取消跳轉(zhuǎn),WKNavigationActionPolicyAllow允許跳轉(zhuǎn)
    
    NSLog(@"3-------在收到響應(yīng)后,決定是否跳轉(zhuǎn)");
    
    decisionHandler(WKNavigationResponsePolicyAllow);
}

// 4 當(dāng)內(nèi)容開始返回時調(diào)用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
    NSLog(@"4-------當(dāng)內(nèi)容開始返回時調(diào)用");
}

// 5 頁面加載完成之后調(diào)用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
    NSLog(@"5-------頁面加載完成之后調(diào)用");
}

// 6 頁面加載失敗時調(diào)用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation {
    
    NSLog(@"6-------頁面加載失敗時調(diào)用");
}

// 接收到服務(wù)器跳轉(zhuǎn)請求之后調(diào)用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
    NSLog(@"-------接收到服務(wù)器跳轉(zhuǎn)請求之后調(diào)用");
}

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

// 需要響應(yīng)身份驗(yàn)證時調(diào)用 同樣在block中需要傳入用戶身份憑證
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
    //用戶身份信息
    
    NSLog(@"----需要響應(yīng)身份驗(yàn)證時調(diào)用 同樣在block中需要傳入用戶身份憑證");
    
    NSURLCredential *newCred = [NSURLCredential credentialWithUser:@""
                                                          password:@""
                                                       persistence:NSURLCredentialPersistenceNone];
    // 為 challenge 的發(fā)送方提供 credential
    [[challenge sender] useCredential:newCred forAuthenticationChallenge:challenge];
    completionHandler(NSURLSessionAuthChallengeUseCredential,newCred);
}

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

六、WKUIDelegate代理方法

/**
 *  web界面中有彈出警告框時調(diào)用
 *
 *  @param webView           實(shí)現(xiàn)該代理的webview
 *  @param message           警告框中的內(nèi)容
 *  @param completionHandler 警告框消失調(diào)用
 */
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(void (^)())completionHandler {
    
    NSLog(@"-------web界面中有彈出警告框時調(diào)用");
}

// 創(chuàng)建新的webView時調(diào)用的方法
- (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
    
    NSLog(@"-----創(chuàng)建新的webView時調(diào)用的方法");
    return webView;
}

// 關(guān)閉webView時調(diào)用的方法
- (void)webViewDidClose:(WKWebView *)webView {
    
    NSLog(@"----關(guān)閉webView時調(diào)用的方法");
}

// 下面這些方法是交互JavaScript的方法
// JavaScript調(diào)用confirm方法后回調(diào)的方法 confirm是js中的確定框,需要在block中把用戶選擇的情況傳遞進(jìn)去
-(void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler {
    
    NSLog(@"%@",message);
    completionHandler(YES);
}
// JavaScript調(diào)用prompt方法后回調(diào)的方法 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");
}

// 默認(rèn)預(yù)覽元素調(diào)用
- (BOOL)webView:(WKWebView *)webView shouldPreviewElement:(WKPreviewElementInfo *)elementInfo {
    
    NSLog(@"-----默認(rèn)預(yù)覽元素調(diào)用");
    return YES;
}

// 返回一個視圖控制器將導(dǎo)致視圖控制器被顯示為一個預(yù)覽。返回nil將WebKit的默認(rèn)預(yù)覽的行為。
- (nullable UIViewController *)webView:(WKWebView *)webView previewingViewControllerForElement:(WKPreviewElementInfo *)elementInfo defaultActions:(NSArray<id <WKPreviewActionItem>> *)previewActions {
    
    NSLog(@"----返回一個視圖控制器將導(dǎo)致視圖控制器被顯示為一個預(yù)覽。返回nil將WebKit的默認(rèn)預(yù)覽的行為。");
    return self;
}

// 允許應(yīng)用程序向它創(chuàng)建的視圖控制器彈出
- (void)webView:(WKWebView *)webView commitPreviewingViewController:(UIViewController *)previewingViewController {
    
    NSLog(@"----允許應(yīng)用程序向它創(chuàng)建的視圖控制器彈出");
    
}

// 顯示一個文件上傳面板。completionhandler完成處理程序調(diào)用后打開面板已被撤銷。通過選擇的網(wǎng)址,如果用戶選擇確定,否則為零。如果不實(shí)現(xiàn)此方法,Web視圖將表現(xiàn)為如果用戶選擇了取消按鈕。
- (void)webView:(WKWebView *)webView runOpenPanelWithParameters:(WKOpenPanelParameters *)parameters initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSArray<NSURL *> * _Nullable URLs))completionHandler {
    
    NSLog(@"----顯示一個文件上傳面板");
    
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,702評論 2 370

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