iOS中WebKit框架應用與解析

iOS中WebKit框架應用與解析

1、引言

        在iOS8以前,在應用中嵌入網頁一般須要使用UIWebView這樣一個類,這個類經過URL或者HTML文件來加載網頁視圖,功能十分有限,只能做爲輔助嵌入原生應用程序中。雖然UIWebView也能夠作原生與JavaScript交互的相關處理,然而也有很大的侷限性,JavaScript要調用原生方法一般須要約定好協議以後經過Request來傳遞。WebKit框架中添加了一些原生與JavaScript交互的方法,加強了網頁視圖與原生的交互能力。而且WebKit框架中採用導航堆棧的模型來管理網頁的跳轉,開發者也能夠更加容易的控制和管理網頁的渲染。關於UIWebView的相關使用,在前面的博客中有詳細介紹,地址以下。java

UIWebView的使用詳解:http://my.oschina.net/u/2340880/blog/469916web

2、WebKit框架概覽

        WebKit框架中涉及的類不少,框架的設計十分面向對象和模塊化,開發者在使用時能夠輕鬆的寫出結構清晰的代碼。在進行使用前,咱們首先應該清楚整個框架的結構和開發思路,下面一張腦圖中基本列出了WebKit框架中所涉及到的全部重要的類以及他們之間的相互關係:數組

如上圖所示,WebKit框架中最核心的類應該屬於WKWebView了,這個類專門用來渲染網頁視圖,其餘類和協議都將基於它和服務於它。緩存

WKWebView:網頁的渲染與展現,經過WKWebViewConfiguration能夠進行配置。安全

WKWebViewConfiguration:這個類專門用來配置WKWebView。app

WKPreference:這個類用來進行M相關設置。框架

WKProcessPool:這個類用來配置進程池,與網頁視圖的資源共享有關。ide

WKUserContentController:這個類主要用來作native與JavaScript的交互管理。模塊化

WKUserScript:用於進行JavaScript注入。post

WKScriptMessageHandler:這個類專門用來處理JavaScript調用native的方法。

WKNavigationDelegate:網頁跳轉間的導航管理協議,這個協議能夠監聽網頁的活動。

WKNavigationAction:網頁某個活動的示例化對象。

WKUIDelegate:用於交互處理JavaScript中的一些彈出框。

WKBackForwardList:堆棧管理的網頁列表。

WKBackForwardListItem:每一個網頁節點對象。

3、使用WKWebViewConfiguration對WebView進行配置

        使用下面的代碼能夠建立一個WKWebView視圖,建立WebView視圖時,須要使用WKWebViewConfiguration來進行配置:

WKWebView * WK;
    WKWebViewConfiguration * config = [[WKWebViewConfiguration alloc]init];
    WK = [[WKWebView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height-40) configuration:config];
    [WK loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]]];

WKWebViewConfiguration中能夠進行配置的方法和屬性以下:

//設置進程池
    WKProcessPool * pool = [[WKProcessPool alloc]init];
    config.processPool = pool;

WKProcessPool類中沒有暴露任何屬性和方法,配置爲同一個進程池的WebView會共享數據,例如Cookie、用戶憑證等,開發者能夠經過編寫管理類來分配不一樣維度的WebView在不一樣進程池中。

//進行偏好設置
    WKPreferences * preference = [[WKPreferences alloc]init];
    //最小字體大小 當將javaScriptEnabled屬性設置爲NO時,能夠看到明顯的效果
    preference.minimumFontSize = 0;
    //設置是否支持javaScript 默認是支持的
    preference.javaScriptEnabled = YES;
    //設置是否容許不通過用戶交互由javaScript自動打開窗口
    preference.javaScriptCanOpenWindowsAutomatically = YES;
    config.preferences = preference;

WKPerference實例爲WebView提供一個偏好設置。

//設置內容交互控制器 用於處理JavaScript與native交互
    WKUserContentController * userController = [[WKUserContentController alloc]init];
    //設置處理代理而且註冊要被js調用的方法名稱
    [userController addScriptMessageHandler:self name:@"name"];
    //js注入,注入一個測試方法。
    NSString *javaScriptSource = @"function userFunc(){window.webkit.messageHandlers.name.postMessage( {\"name\":\"HS\"})}";
    WKUserScript *userScript = [[WKUserScript alloc] initWithSource:javaScriptSource injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];// forMainFrameOnly:NO(全局窗口),yes(只限主窗口)
    [userController addUserScript:userScript];
    config.userContentController = userController;

WKUserContentController專門用來管理native與JavaScript的交互行爲,addScriptMessageHandler:name:方法來註冊要被js調用的方法名稱,以後再JavaScript中使用window.webkit.messageHandlers.name.postMessage()方法來像native發送消息,支持OC中字典,數組,NSNumber等原生數據類型,JavaScript代碼中的name要和上面註冊的相同。在native代理的回調方法中,會獲取到JavaScript傳遞進來的消息,以下:

-(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
    //這裏能夠獲取到JavaScript傳遞進來的消息
}

WKScriptMessage類是JavaScript傳遞的對象實例,其中屬性以下:

//傳遞的消息主體
@property (nonatomic, readonly, copy) id body;
//傳遞消息的WebView
@property (nullable, nonatomic, readonly, weak) WKWebView *webView;
//傳遞消息的WebView當前頁面對象
@property (nonatomic, readonly, copy) WKFrameInfo *frameInfo;
//消息名稱
@property (nonatomic, readonly, copy) NSString *name;

WKUserContentController實例的addUserScript:用於注入JavaScript代碼,後面會專門介紹。

//設置數據存儲store
    config.websiteDataStore = [WKWebsiteDataStore defaultDataStore];

WebKit框架採用其自己的緩存框架,WKWebsiteDataStore類用來處理數據的存儲,其中屬性和方法以下:

@interface WKWebsiteDataStore : NSObject
//獲取默認的存儲器 此存儲器爲持久性的會被寫入磁盤
+ (WKWebsiteDataStore *)defaultDataStore;
//獲取一個臨時的存儲器
+ (WKWebsiteDataStore *)nonPersistentDataStore;
//存儲器是不是臨時的
@property (nonatomic, readonly, getter=isPersistent) BOOL persistent;
//全部能夠存儲的類型
+ (NSSet<NSString *> *)allWebsiteDataTypes;
@end
//設置是否將網頁內容所有加載到內存後再渲染
    config.suppressesIncrementalRendering = NO;
    //設置HTML5視頻是否容許網頁播放 設置爲NO則會使用本地播放器
    config.allowsInlineMediaPlayback =  YES;
    //設置是否容許ariPlay播放
    config.allowsAirPlayForMediaPlayback = YES;
    //設置視頻是否須要用戶手動播放  設置爲NO則會容許自動播放
    config.requiresUserActionForMediaPlayback = NO;
    //設置是否容許畫中畫技術 在特定設備上有效
    config.allowsPictureInPictureMediaPlayback = YES;
    //設置選擇模式 是按字符選擇 仍是按模塊選擇
    /*
    typedef NS_ENUM(NSInteger, WKSelectionGranularity) {
        //按模塊選擇
        WKSelectionGranularityDynamic,
        //按字符選擇
        WKSelectionGranularityCharacter,
    } NS_ENUM_AVAILABLE_IOS(8_0);
    */
    config.selectionGranularity = WKSelectionGranularityCharacter;
    //設置請求的User-Agent信息中應用程序名稱 iOS9後可用
    config.applicationNameForUserAgent = @"HS";

4、WKWebView中的屬性和方法解析

        下面列舉了WKWebView中經常使用的屬性和方法。

//設置導航代理
@property (nullable, nonatomic, weak) id <WKNavigationDelegate> navigationDelegate;
//設置UI代理
@property (nullable, nonatomic, weak) id <WKUIDelegate> UIDelegate;
//導航列表
@property (nonatomic, readonly, strong) WKBackForwardList *backForwardList;
//經過url加載網頁視圖
- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;
//經過文件加載網頁視圖
- (nullable WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL NS_AVAILABLE(10_11, 9_0);
//經過HTML字符串加載網頁視圖
- (nullable WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
//經過data數據加載網頁視圖
- (nullable WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL NS_AVAILABLE(10_11, 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;
//加載進度 能夠監聽這個屬性的值配合UIProgressView來設計進度條
@property (nonatomic, readonly) double estimatedProgress;
//是否所有是安全鏈接
@property (nonatomic, readonly) BOOL hasOnlySecureContent;
//證書列表
@property (nonatomic, readonly, copy) NSArray *certificateChain;
//是否能夠回退
@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;
//是否容許右滑返回手勢
@property (nonatomic) BOOL allowsBackForwardNavigationGestures;

WKBackForwardList類爲導航管理的網頁列表類,其中屬性方法意義以下:

@interface WKBackForwardList : NSObject
//當前所在的網頁節點
@property (nullable, nonatomic, readonly, strong) WKBackForwardListItem *currentItem;
//前進的一個網頁節點
@property (nullable, nonatomic, readonly, strong) WKBackForwardListItem *forwardItem;
//回退的一個網頁節點
@property (nullable, nonatomic, readonly, strong) WKBackForwardListItem *backItem;
//獲取某個index的網頁節點
- (nullable WKBackForwardListItem *)itemAtIndex:(NSInteger)index;
//獲取回退的節點數組
@property (nonatomic, readonly, copy) NSArray<WKBackForwardListItem *> *backList;
//獲取前進的節點數組
@property (nonatomic, readonly, copy) NSArray<WKBackForwardListItem *> *forwardList;
@end

在WebKit中,網頁節點被抽象成爲了WKBackForwardListItem類,這個類中封裝的屬性以下:

@interface WKBackForwardListItem : NSObject
//當前節點的URL
@property (readonly, copy) NSURL *URL;
//當前節點的標題
@property (nullable, readonly, copy) NSString *title;
//建立此WebView的初始URL
@property (readonly, copy) NSURL *initialURL;

5、關於native與JavaScript交互

        WebKit中的native與JavaScript的交互主要有4類。

1.JavaScript調用native方法

        這種方式是由WKUserContentController註冊,並在代理方法中實現的。

2.native調用JavaScript方法

        這種方式經過WKWebView直接調用evaluteJavaScript:completionHandler:方法來實現。

3.將JavaScript代碼注入

        這種方式能夠在網頁中注入一些自定義的JavaScript代碼,也能夠注入自定義的方法,再使用evaluteJavaScript:completionHandler:來調用方法。JavaScript代碼的注入也是經過WKUserContentController來完成的,使用addUserScript:方法來注入JavaScript,其中須要經過WKUserScript類來生成要注入的對象,這個類使用以下方法來進行實例化:

/*
source爲要注入的js代碼
WKUserScriptInjectionTime設置注入的時機
forMainFrameOnly參數設置是否只在主頁面注入
typedef NS_ENUM(NSInteger, WKUserScriptInjectionTime) {
    //原js代碼運行前注入
    WKUserScriptInjectionTimeAtDocumentStart,
    //原js代碼運行後注入
    WKUserScriptInjectionTimeAtDocumentEnd
} NS_ENUM_AVAILABLE(10_10, 8_0);

*/
- (instancetype)initWithSource:(NSString *)source injectionTime:(WKUserScriptInjectionTime)injectionTime forMainFrameOnly:(BOOL)forMainFrameOnly;

4.經過WKUIDelegate來交互

        這種方式主要用於相應JavaScript中的彈出框,後面會詳細介紹這個協議。

6、WKNavagationDelegate中方法解析

        WKNavagationDelegate協議重要有兩個做用,監聽頁面渲染流程與控制頁面跳轉,其中方法以下:

/*
決定是否響應網頁的某個動做,例如加載,回退,前進,刷新等,在這個方法中,必須執行decisionHandler()代碼塊,並將是否容許這個活動執行在block中進行傳入
*/
/*
WKNavigationAction是網頁動做的抽象化,其中封裝了許多行爲信息,後面會介紹
WKNavigationActionPolicy爲開發者回執,枚舉以下:
typedef NS_ENUM(NSInteger, WKNavigationActionPolicy) {
    //取消這次行爲
    WKNavigationActionPolicyCancel,
    //容許這次行爲
    WKNavigationActionPolicyAllow,
} NS_ENUM_AVAILABLE(10_10, 8_0);
*/
-(void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
    decisionHandler(WKNavigationActionPolicyAllow);
}
//須要響應身份驗證時調用 一樣在block中須要傳入用戶身份憑證
-(void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler{
    //用戶身份信息
    NSURLCredential *newCred = [NSURLCredential credentialWithUser:@""
                                                          password:@""
                                                       persistence:NSURLCredentialPersistenceNone];
    // 爲 challenge 的發送方提供 credential
    [[challenge sender] useCredential:newCred
           forAuthenticationChallenge:challenge];
    completionHandler(NSURLSessionAuthChallengeUseCredential,newCred);
}
//接收到數據後是否容許執行渲染
/*
其中,WKNavigationResponse爲請求回執信息
WKNavigationResponsePokicy爲開發者回執,枚舉以下:
typedef NS_ENUM(NSInteger, WKNavigationResponsePolicy) {
    //取消渲染
    WKNavigationResponsePolicyCancel,
    //容許渲染
    WKNavigationResponsePolicyAllow,
} NS_ENUM_AVAILABLE(10_10, 8_0);
*/
-(void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
    decisionHandler(WKNavigationResponsePolicyAllow);
}
//=====================下面這個協議方法用於監聽流程=========================================
//頁面加載啓動時調用
-(void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{

}
//當主機接收到的服務重定向時調用
-(void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation{

}
//內容到達主機時調用
-(void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation{

}
//主頁加載完成時調用
-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{

}
//提交發生錯誤時調用
-(void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error{

}
//主頁數據加載發生錯誤時調用
-(void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(nonnull NSError *)error{

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

}

7、WKUIDelegate協議中方法解析

//建立新的webView時調用的方法
-(WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures{
    return webView;
}
//關閉webView時調用的方法
-(void)webViewDidClose:(WKWebView *)webView{

}
//下面這些方法是交互JavaScript的方法
//JavaScript調用alert方法後回調的方法 message中爲alert提示的信息 必需要在其中調用completionHandler()
-(void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{
    NSLog(@"%@",message);
    completionHandler();
}
//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");
}

8、擴展

        首先,在註冊要被JavaScript調用的方法時須要設置代理,在不須要時須要將代理移除,WKUserContentController中也提供了移除這個代理的方法,若是不移除,將會形成WebView不能釋放。方法以下:

//註冊一個監聽方法
- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;
//移除一個方法的監聽
- (void)removeScriptMessageHandlerForName:(NSString *)name;

一樣與注入JavaScript對應,也能夠將注入的代碼移除,方法以下:

//注入一個JavaScript抽象對象
- (void)addUserScript:(WKUserScript *)userScript;
//移除全部注入
- (void)removeAllUserScripts;

        在上面,常常會見到WKNavagationAction這個類,這個類中封裝的是一些頁面活動信息,以下:

@interface WKNavigationAction : NSObject
//原頁面
@property (nonatomic, readonly, copy) WKFrameInfo *sourceFrame;
//目標頁面
@property (nullable, nonatomic, readonly, copy) WKFrameInfo *targetFrame;
//請求URL
@property (nonatomic, readonly, copy) NSURLRequest *request;
//活動類型
/*
typedef NS_ENUM(NSInteger, WKNavigationType) {
    //連接激活
    WKNavigationTypeLinkActivated,
    //提交操做
    WKNavigationTypeFormSubmitted,
    //前進操做
    WKNavigationTypeBackForward,
    //刷新操做
    WKNavigationTypeReload,
    //重提交操做 例如前進 後退 刷新
    WKNavigationTypeFormResubmitted,
    //其餘類型
    WKNavigationTypeOther = -1,
} NS_ENUM_AVAILABLE(10_10, 8_0);
*/
@property (nonatomic, readonly) WKNavigationType navigationType;
@end

 

專一技術,熱愛生活,交流技術,也作朋友。

——琿少 QQ羣:203317592

相關文章
相關標籤/搜索