WKWebView介紹
主要類:
- WKWebView
- WKWebViewConfiguration:
- WKUserScript:
- WKUserContentController:
- WKWebsiteDataStore:
主要代理:
- WKNavigationDelegate
- WKUIDelegate
1. WKWebView
1.1 常用屬性
// 導航代理
@property (nullable, nonatomic, weak) id <WKNavigationDelegate> navigationDelegate;
// UI代理
@property (nullable, nonatomic, weak) id <WKUIDelegate> UIDelegate;
// 頁面標題, 一般使用KVO動態(tài)獲取
@property (nullable, nonatomic, readonly, copy) NSString *title;
// 頁面加載進度, 一般使用KVO動態(tài)獲取
@property (nonatomic, readonly) double estimatedProgress;
// 可返回的頁面列表, 已打開過的網(wǎng)頁, 有點類似于navigationController的viewControllers屬性
@property (nonatomic, readonly, strong) WKBackForwardList *backForwardList;
// 頁面url
@property (nullable, nonatomic, readonly, copy) NSURL *URL;
// 頁面是否在加載中
@property (nonatomic, readonly, getter=isLoading) BOOL loading;
// 是否可返回
@property (nonatomic, readonly) BOOL canGoBack;
// 是否可向前
@property (nonatomic, readonly) BOOL canGoForward;
// WKWebView繼承自UIView, 所以如果想設置scrollView的一些屬性, 需要對此屬性進行配置
@property (nonatomic, readonly, strong) UIScrollView *scrollView;
// 是否允許手勢左滑返回上一級, 類似導航控制的左滑返回
@property (nonatomic) BOOL allowsBackForwardNavigationGestures;
//自定義UserAgent, 會覆蓋默認的值 ,iOS 9之后有效
@property (nullable, nonatomic, copy) NSString *customUserAgent
1.2 常用方法
// 帶配置信息的初始化方法
// configuration 配置信息
- (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration
// 加載請求
- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;
// 加載HTML
- (nullable WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
// 返回上一級
- (nullable WKNavigation *)goBack;
// 前進下一級, 需要曾經(jīng)打開過, 才能前進
- (nullable WKNavigation *)goForward;
// 刷新頁面
- (nullable WKNavigation *)reload;
// 根據(jù)緩存有效期來刷新頁面
- (nullable WKNavigation *)reloadFromOrigin;
// 停止加載頁面
- (void)stopLoading;
// 執(zhí)行JavaScript代碼
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;
2. WKWebViewConfiguration
網(wǎng)頁配置模型钢拧,僅在初始化時可設置壹无,初始化后無法修改垦写。
/*webview的進程池,與頁面的資源存儲有關
Tips:前端使用 localstorage 進行存儲惠拭,A頁面和B頁面都存在 一個WKWebView莫杈,在B頁面使用localstorage保存信息蛙婴,回到A頁面取不到最新的數(shù)據(jù)敷钾。
解決方案:將processPool設置成單例。
*/
@property (nonatomic, strong) WKProcessPool *processPool;
// webview 偏好設置愈犹,可設置最小字體等屬性
@property (nonatomic, strong) WKPreferences *preferences;
// 專門用來管理native與JavaScript的交互行為
@property (nonatomic, strong) WKUserContentController *userContentController;
// web持久化键科,包括 cookie、磁盤和內(nèi)存緩存以及持久性數(shù)據(jù)漩怎,例如 WebSQL勋颖、IndexedDB 數(shù)據(jù)庫和本地存儲
@property (nonatomic, strong) WKWebsiteDataStore *websiteDataStore API_AVAILABLE(macos(10.11), ios(9.0));
// 是否在所有的數(shù)據(jù)加載到內(nèi)存中才開始渲染,默認為 NO扬卷。加載網(wǎng)頁時牙言,網(wǎng)頁會逐漸呈現(xiàn),設置為true怪得,僅在加載所有內(nèi)容后才顯示網(wǎng)頁咱枉。
@property (nonatomic) BOOL suppressesIncrementalRendering;
// 設置User-Agent最后的名字,用于標記應用徒恋。
@property (nullable, nonatomic, copy) NSString *applicationNameForUserAgent API_AVAILABLE(macos(10.11), ios(9.0));
// 是否允許播放媒體文件蚕断,默認為YES
@property (nonatomic) BOOL allowsAirPlayForMediaPlayback API_AVAILABLE(macos(10.11), ios(9.0));
// 指對已知支持 HTTPS 的服務器的 HTTP 請求是否應自動升級為 HTTPS 請求。默認為YES
@property (nonatomic) BOOL upgradeKnownHostsToHTTPS API_AVAILABLE(macos(11.3), ios(14.5));
// 網(wǎng)頁中的多媒體是否需要手勢才能開始播放(iOS 10) 可以設置僅音頻需要入挣、僅視頻需要等四種狀態(tài)
@property (nonatomic) WKAudiovisualMediaTypes mediaTypesRequiringUserActionForPlayback API_AVAILABLE(macos(10.12), ios(10.0));
// 決定了用內(nèi)嵌HTML5播放視頻還是用本地的全屏控制亿乳。默認為NO
@property (nonatomic) BOOL allowsInlineMediaPlayback;
// 控制用戶與webview進行選擇交互時的粒度,可以選擇整個塊兒径筏,或單個符號.
@property (nonatomic) WKSelectionGranularity selectionGranularity;
3. WKUserContentController
WKUserContentController 是JavaScript與原生進行交互的橋梁葛假,主要方法:
// JS 端可通過 window.webkit.messageHandlers.<name>.postMessage(<messageBody>) 發(fā)送消息
- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;
// 移除注入的協(xié)議, 在deinit方法中調(diào)用
- (void)removeScriptMessageHandlerForName:(NSString *)name;
// 通過WKUserScript注入需要執(zhí)行的JavaScript代碼
- (void)addUserScript:(WKUserScript *)userScript;
// 移除所有注入的JavaScript代碼
- (void)removeAllUserScripts;
可通過
addScriptMessageHandler:name:
方法注冊JS與Native交互名字,可通過addUserScript :
注入需要執(zhí)行的JS代碼滋恬。
3.1 JavaScript調(diào)用Native
Native代碼:
#pragma mark - Native配置代碼
//創(chuàng)建網(wǎng)頁配置對象
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
//自定義的WKScriptMessageHandler 是為了解決內(nèi)存不釋放的問題
WeakWebViewScriptMessageDelegate *weakScriptMessageDelegate = [[WeakWebViewScriptMessageDelegate alloc] initWithDelegate:self];
//這個類主要用來做native與JavaScript的交互管理
WKUserContentController * wkUController = [[WKUserContentController alloc] init];
//注冊一個name為jsToOcNoPrams的js方法 設置處理接收JS方法的對象
[wkUController addScriptMessageHandler:weakScriptMessageDelegate name:@"jsToNative"];
config.userContentController = wkUController;
#pragma mark - Native代理
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
NSLog(@"name:%@\\\\n body:%@\\\\n frameInfo:%@\\\\n",message.name,message.body,message.frameInfo);
//用message.body獲得JS傳出的參數(shù)體
NSDictionary * parameter = message.body;
//JS調(diào)用OC
if([message.name isEqualToString:@"jsToNative"]){
// do something
}
}
JavaScript代碼:
window.webkit.messageHandlers.jsToNative.postMessage({"params":"我是參數(shù)"});
4. WKUserScript
在WebKit框架中聊训,我們還可以預先添加JS方法,供其他人員調(diào)用恢氯。WKUserScript就是幫助我們完成JS注入的類带斑,它能幫助我們在頁面填充前或js填充完成后調(diào)用鼓寺。
//以下代碼適配文本大小
NSString *jSString = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";
// NSString *jSString = @"alert(\"WKUserScript注入js\");";
//用于進行JavaScript注入
WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jSString injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
[config.userContentController addUserScript:wkUScript];
5. WKUIDelegate
這個代理方法, 主要是用來處理使用系統(tǒng)的彈框來替換JS中的一些彈框的,比如: 警告框, 選擇框, 輸入框, 主要使用的是下面三個代理方法:
/** 對應js的alert方法
webView中彈出警告框時調(diào)用, 只能有一個按鈕
@param webView webView
@param message 提示信息
@param frame 可用于區(qū)分哪個窗口調(diào)用的
@param completionHandler 警告框消失的時候調(diào)用, 回調(diào)給JS
*/
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"警告" message:message preferredStyle:(UIAlertControllerStyleAlert)];
UIAlertAction *ok = [UIAlertAction actionWithTitle:@"我知道了" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
completionHandler();
}];
[alert addAction:ok];
[self presentViewController:alert animated:YES completion:nil];
}
/** 對應js的confirm方法
webView中彈出選擇框時調(diào)用, 兩個按鈕
@param webView webView description
@param message 提示信息
@param frame 可用于區(qū)分哪個窗口調(diào)用的
@param completionHandler 確認框消失的時候調(diào)用, 回調(diào)給JS, 參數(shù)為選擇結果: YES or NO
*/
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"請選擇" message:message preferredStyle:(UIAlertControllerStyleAlert)];
UIAlertAction *ok = [UIAlertAction actionWithTitle:@"同意" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
completionHandler(YES);
}];
UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"不同意" style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {
completionHandler(NO);
}];
[alert addAction:ok];
[alert addAction:cancel];
[self presentViewController:alert animated:YES completion:nil];
}
/** 對應js的prompt方法
webView中彈出輸入框時調(diào)用, 兩個按鈕 和 一個輸入框
@param webView webView description
@param prompt 提示信息
@param defaultText 默認提示文本
@param frame 可用于區(qū)分哪個窗口調(diào)用的
@param completionHandler 輸入框消失的時候調(diào)用, 回調(diào)給JS, 參數(shù)為輸入的內(nèi)容
*/
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"請輸入" message:prompt preferredStyle:(UIAlertControllerStyleAlert)];
[alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.placeholder = @"請輸入";
}];
UIAlertAction *ok = [UIAlertAction actionWithTitle:@"確定" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
UITextField *tf = [alert.textFields firstObject];
completionHandler(tf.text);
}];
UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"取消" style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {
completionHandler(defaultText);
}];
[alert addAction:ok];
[alert addAction:cancel];
[self presentViewController:alert animated:YES completion:nil];
}
// 頁面是彈出窗口 _blank 處理
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
if (!navigationAction.targetFrame.isMainFrame) {
[webView loadRequest:navigationAction.request];
}
return nil;
}
6. WKNavigationDelegate
主要處理一些跳轉(zhuǎn)、加載處理操作勋磕。
// main frame的導航開始請求時調(diào)用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation{
NSLog(@"開始加載");
}
// 當main frame開始加載數(shù)據(jù)失敗時妈候,會回調(diào)
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
[self.progressView setProgress:0.0f animated:NO];
}
// 當內(nèi)容開始返回時調(diào)用
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation{
}
//當main frame導航完成時,會回調(diào)
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation{
// 頁面加載完成之后調(diào)用
}
// 當main frame最后下載數(shù)據(jù)失敗時挂滓,會回調(diào)
- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
[self.progressView setProgress:0.0f animated:NO];
}
// 當main frame接收到服務重定向時調(diào)用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation{
// 接收到服務器跳轉(zhuǎn)請求之后調(diào)用
}
// 根據(jù)WebView對于即將跳轉(zhuǎn)的HTTP請求頭信息和相關信息來決定是否跳轉(zhuǎn)
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSString * urlStr = navigationAction.request.URL.absoluteString;
NSLog(@"發(fā)送跳轉(zhuǎn)請求:%@",urlStr);
//自己定義的協(xié)議頭
NSString *htmlHeadString = @"github://";
if([urlStr hasPrefix:htmlHeadString]){
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"通過截取URL調(diào)用OC" message:@"你想前往我的Github主頁?" preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:([UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
}])];
[alertController addAction:([UIAlertAction actionWithTitle:@"打開" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSURL * url = [NSURL URLWithString:[urlStr stringByReplacingOccurrencesOfString:@"github://callName_?" withString:@""]];
[[UIApplication sharedApplication] openURL:url];
}])];
[self presentViewController:alertController animated:YES completion:nil];
decisionHandler(WKNavigationActionPolicyCancel);
}else{
decisionHandler(WKNavigationActionPolicyAllow);
}
}
// 根據(jù)客戶端受到的服務器響應頭以及response相關信息來決定是否可以跳轉(zhuǎn)
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
NSString * urlStr = navigationResponse.response.URL.absoluteString;
NSLog(@"當前跳轉(zhuǎn)地址:%@",urlStr);
//允許跳轉(zhuǎn)
decisionHandler(WKNavigationResponsePolicyAllow);
//不允許跳轉(zhuǎn)
//decisionHandler(WKNavigationResponsePolicyCancel);
}
//用于授權驗證的API苦银,與AFN、UIWebView的授權驗證API是一樣的
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *__nullable credential))completionHandler{
//用戶身份信息
NSURLCredential * newCred = [[NSURLCredential alloc] initWithUser:@"user123" password:@"123" persistence:NSURLCredentialPersistenceNone];
//為 challenge 的發(fā)送方提供 credential
[challenge.sender useCredential:newCred forAuthenticationChallenge:challenge];
completionHandler(NSURLSessionAuthChallengeUseCredential,newCred);
}
// 當web content處理完成時杂彭,會回調(diào)
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView {
}
7. WKWebsiteDataStore
WKWebsiteDataStore 提供了網(wǎng)站所能使用的數(shù)據(jù)類型墓毒,包括 cookies,硬盤緩存亲怠,內(nèi)存緩存活在一些WebSQL的數(shù)據(jù)持久化和本地持久化∧牵可通過 WKWebViewConfiguration 類的屬性 websiteDataStore 進行相關的設置团秽。
// 默認的data store
+ (WKWebsiteDataStore *)defaultDataStore;
// 如果為webView設置了這個data Store,則不會有數(shù)據(jù)緩存被寫入文件
// 當需要實現(xiàn)隱私瀏覽的時候叭首,可使用這個
+ (WKWebsiteDataStore *)nonPersistentDataStore;
// 是否是可緩存數(shù)據(jù)的习勤,只讀
@property (nonatomic, readonly, getter=isPersistent) BOOL persistent;
// 獲取所有可使用的數(shù)據(jù)類型
+ (NSSet<NSString *> *)allWebsiteDataTypes;
// 查找指定類型的緩存數(shù)據(jù)
// 回調(diào)的值是WKWebsiteDataRecord的集合
- (void)fetchDataRecordsOfTypes:(NSSet<NSString *> *)dataTypes completionHandler:(void (^)(NSArray<WKWebsiteDataRecord *> *))completionHandler;
// 刪除指定的紀錄
// 這里的參數(shù)是通過上面的方法查找到的WKWebsiteDataRecord實例獲取的
- (void)removeDataOfTypes:(NSSet<NSString *> *)dataTypes forDataRecords:(NSArray<WKWebsiteDataRecord *> *)dataRecords completionHandler:(void (^)(void))completionHandler;
// 刪除某時間后修改的某類型的數(shù)據(jù)
- (void)removeDataOfTypes:(NSSet<NSString *> *)websiteDataTypes modifiedSince:(NSDate *)date completionHandler:(void (^)(void))completionHandler;
// 保存的HTTP cookies
@property (nonatomic, readonly) WKHTTPCookieStore *httpCookieStore
7.1 WebsiteDataTypes
// 硬盤緩存
WKWebsiteDataTypeDiskCache,
// HTML離線web應用程序緩存
WKWebsiteDataTypeOfflineWebApplicationCache,
// 內(nèi)存緩存
WKWebsiteDataTypeMemoryCache,
// 本地緩存
WKWebsiteDataTypeLocalStorage,
// cookies
WKWebsiteDataTypeCookies,
// HTML會話存儲
WKWebsiteDataTypeSessionStorage,
// IndexedDB 數(shù)據(jù)庫
WKWebsiteDataTypeIndexedDBDatabases,
// WebSQL 數(shù)據(jù)庫
WKWebsiteDataTypeWebSQLDatabases
7.2 WKHTTPCookieStore
用于管理WKWebView中的Cookie
/*! 查找所有已存儲的cookie
*/
- (void)getAllCookies:(void (^)(NSArray<NSHTTPCookie *> *))completionHandler;
/*! 保存一個cookie, 保存成功后, 會走一次回調(diào)方法
*/
- (void)setCookie:(NSHTTPCookie *)cookie completionHandler:(nullable void (^)(void))completionHandler;
/*! 刪除一個cookie, 待刪除的cookie對象可通過 'getAllCookies' 方法獲取
*/
- (void)deleteCookie:(NSHTTPCookie *)cookie completionHandler:(nullable void (^)(void))completionHandler;
/*! 添加一個觀察者, 需要遵循協(xié)議 WKHTTPCookieStoreObserver
當cookie發(fā)送變化時, 會通過 WKHTTPCookieStoreObserver 的協(xié)議方法通知該觀察者, 在使用完后需要移除觀察者
*/
- (void)addObserver:(id<WKHTTPCookieStoreObserver>)observer;
/*! 移除觀察者
*/
- (void)removeObserver:(id<WKHTTPCookieStoreObserver>)observer;