1. WKWebView
1.1 常用屬性
// 導(dǎo)航代理
@property (nullable, nonatomic, weak) id <WKNavigationDelegate> navigationDelegate;
// UI代理
@property (nullable, nonatomic, weak) id <WKUIDelegate> UIDelegate;
// 頁面標(biāo)題, 一般使用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, 所以如果想設(shè)置scrollView的一些屬性, 需要對此屬性進行配置
@property (nonatomic, readonly, strong) UIScrollView *scrollView;
// 是否允許手勢左滑返回上一級, 類似導(dǎo)航控制的左滑返回
@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
// 通過此屬性來執(zhí)行JavaScript代碼來修改頁面的行為
@property (nonatomic, strong) WKUserContentController *userContentController;
//***********下面屬性一般不需要設(shè)置
// 首選項設(shè)置,
//可設(shè)置最小字號, 是否允許執(zhí)行js
//是否通過js自動打開新的窗口
@property (nonatomic, strong) WKPreferences *preferences;
// 是否允許播放媒體文件
@property (nonatomic) BOOL allowsAirPlayForMediaPlayback
// 需要用戶來操作才能播放的多媒體類型
@property (nonatomic) WKAudiovisualMediaTypes mediaTypesRequiringUserActionForPlayback
// 是使用h5的視頻播放器在線播放, 還是使用原生播放器全屏播放
@property (nonatomic) BOOL allowsInlineMediaPlayback;
3. WKUserContentController
WKUserContentController 是JavaScript與原生進行交互的橋梁, 主要使用的方法有:
// 注入JavaScript與原生交互協(xié)議
// 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;
使用WKUserContentController注入的交互協(xié)議, 需要遵循WKScriptMessageHandler協(xié)議, 在其協(xié)議方法中獲取JavaScript端傳遞的事件和參數(shù):
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;
WKScriptMessage包含了傳遞的協(xié)議名稱及參數(shù), 主要從下面的屬性中獲取:
// 協(xié)議名稱, 即上面的add方法傳遞的name
@property (nonatomic, readonly, copy) NSString *name;
// 傳遞的參數(shù)
@property (nonatomic, readonly, copy) id body;
4. WKUserScript
WKUserScript用于往加載的頁面中添加額外需要執(zhí)行的JavaScript代碼, 主要是一個初始化方法:
/*
source: 需要執(zhí)行的JavaScript代碼
injectionTime: 加入的位置, 是一個枚舉
typedef NS_ENUM(NSInteger, WKUserScriptInjectionTime) {
WKUserScriptInjectionTimeAtDocumentStart,
WKUserScriptInjectionTimeAtDocumentEnd
} API_AVAILABLE(macosx(10.10), ios(8.0));
forMainFrameOnly: 是加入所有框架, 還是只加入主框架
*/
- (instancetype)initWithSource:(NSString *)source injectionTime:(WKUserScriptInjectionTime)injectionTime forMainFrameOnly:(BOOL)forMainFrameOnly;
5. WKUIDelegate
這個代理方法, 主要是用來處理使用系統(tǒng)的彈框來替換JS中的一些彈框的,比如: 警告框, 選擇框, 輸入框, 主要使用的是下面三個代理方法:
/**
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];
}
/** 對應(yīng)js的confirm方法
webView中彈出選擇框時調(diào)用, 兩個按鈕
@param webView webView description
@param message 提示信息
@param frame 可用于區(qū)分哪個窗口調(diào)用的
@param completionHandler 確認框消失的時候調(diào)用, 回調(diào)給JS, 參數(shù)為選擇結(jié)果: 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];
}
/** 對應(yīng)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];
}
6. WKNavigationDelegate
// 決定導(dǎo)航的動作闷畸,通常用于處理跨域的鏈接能否導(dǎo)航涤伐。
// WebKit對跨域進行了安全檢查限制稍浆,不允許跨域,因此我們要對不能跨域的鏈接單獨處理崇摄。
// 但是,對于Safari是允許跨域的,不用這么處理郑叠。
// 這個是決定是否Request
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
// 在發(fā)送請求之前孙技,決定是否跳轉(zhuǎn)
decisionHandler(WKNavigationActionPolicyAllow);
}
// 是否接收響應(yīng)
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
// 在收到響應(yīng)后产禾,決定是否跳轉(zhuǎn)和發(fā)送請求之前那個允許配套使用
decisionHandler(WKNavigationResponsePolicyAllow);
}
//用于授權(quán)驗證的API,與AFN牵啦、UIWebView的授權(quán)驗證API是一樣的
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *__nullable credential))completionHandler{
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling ,nil);
}
// main frame的導(dǎo)航開始請求時調(diào)用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation{
}
// 當(dāng)main frame接收到服務(wù)重定向時調(diào)用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation{
// 接收到服務(wù)器跳轉(zhuǎn)請求之后調(diào)用
}
// 當(dāng)main frame開始加載數(shù)據(jù)失敗時亚情,會回調(diào)
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
}
// 當(dāng)內(nèi)容開始返回時調(diào)用
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation{
}
//當(dāng)main frame導(dǎo)航完成時,會回調(diào)
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation{
// 頁面加載完成之后調(diào)用
}
// 當(dāng)main frame最后下載數(shù)據(jù)失敗時哈雏,會回調(diào)
- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
}
// 當(dāng)web content處理完成時楞件,會回調(diào)
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView {
}
7. WKWebsiteDataStore
WKWebsiteDataStore 提供了網(wǎng)站所能使用的數(shù)據(jù)類型,包括 cookies裳瘪,硬盤緩存履因,內(nèi)存緩存活在一些WebSQL的數(shù)據(jù)持久化和本地持久化№镉蓿可通過 WKWebViewConfiguration 類的屬性 websiteDataStore 進行相關(guān)的設(shè)置栅迄。WKWebsiteDataStore 相關(guān)的API也比較簡單:
// 默認的data store
+ (WKWebsiteDataStore *)defaultDataStore;
// 如果為webView設(shè)置了這個data Store,則不會有數(shù)據(jù)緩存被寫入文件
// 當(dāng)需要實現(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;
// 刪除指定的紀(jì)錄
// 這里的參數(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
PS: 獲取 WKWebsiteDataStore 實例的方法 defaultDataStore/ nonPersistentDataStore ,是有一些區(qū)別的, 具體的可看這篇文章iOS11.3 WKWebView清除cookie所踩的坑
dataTyle
// 硬盤緩存
WKWebsiteDataTypeDiskCache,
// HTML離線web應(yīng)用程序緩存
WKWebsiteDataTypeOfflineWebApplicationCache,
// 內(nèi)存緩存
WKWebsiteDataTypeMemoryCache,
// 本地緩存
WKWebsiteDataTypeLocalStorage,
// cookies
WKWebsiteDataTypeCookies,
// HTML會話存儲
WKWebsiteDataTypeSessionStorage,
// IndexedDB 數(shù)據(jù)庫
WKWebsiteDataTypeIndexedDBDatabases,
// WebSQL 數(shù)據(jù)庫
WKWebsiteDataTypeWebSQLDatabases
WKWebsiteDataRecord
// 展示名稱, 通常是域名
@property (nonatomic, readonly, copy) NSString *displayName;
// 包含的數(shù)據(jù)類型
@property (nonatomic, readonly, copy) NSSet<NSString *> *dataTypes;
WKHTTPCookieStore
關(guān)于cookie, 從WKWebsiteDataStore 的實例對象的數(shù)屬性httpCookieStore 可獲取一個 WKHTTPCookieStore 的實例對象, 通過此對象, 我們可以對cookie進行相關(guān)的操作, 官方提供的API也不難理解:
/*! 查找所有已存儲的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
當(dāng)cookie發(fā)送變化時, 會通過 WKHTTPCookieStoreObserver 的協(xié)議方法通知該觀察者, 在使用完后需要移除觀察者
*/
- (void)addObserver:(id<WKHTTPCookieStoreObserver>)observer;
/*! 移除觀察者
*/
- (void)removeObserver:(id<WKHTTPCookieStoreObserver>)observer;
WKHTTPCookieStoreObserver協(xié)議方法
@protocol WKHTTPCookieStoreObserver <NSObject>
@optional
- (void)cookiesDidChangeInCookieStore:(WKHTTPCookieStore *)cookieStore;
@end
簡單應(yīng)用
刪除指定時間的所有類型數(shù)據(jù)
NSSet *websiteDataTypes = [WKWebsiteDataStore allWebsiteDataTypes];
NSDate *dateFrom = [NSDate dateWithTimeIntervalSince1970:0];
[[WKWebsiteDataStore defaultDataStore] removeDataOfTypes:websiteDataTypes modifiedSince:dateFrom completionHandler:^{
// Done
NSLog(@"釋放");
}];
查找刪除
WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore];
[dataStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] completionHandler:^(NSArray<WKWebsiteDataRecord *> * _Nonnull records) {
for (WKWebsiteDataRecord *record in records) {
[dataStore removeDataOfTypes:record.dataTypes forDataRecords:@[record] completionHandler:^{
// done
}];
}
}];
查找刪除特定的內(nèi)容
WKWebsiteDataStore *dataStore = [WKWebsiteDataStore defaultDataStore];
[dataStore fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] completionHandler:^(NSArray<WKWebsiteDataRecord *> * _Nonnull records) {
for (WKWebsiteDataRecord *record in records) {
if ([record.displayName isEqualToString:@"baidu"]) {
[dataStore removeDataOfTypes:record.dataTypes forDataRecords:@[record] completionHandler:^{
// done
}];
}
}
}];
新增 cookie
NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:@{
NSHTTPCookieName: @"liuhuofeitong",
NSHTTPCookieValue: @"2018",
NSHTTPCookieDomain: @"baidu.com",
NSHTTPCookiePath: @"/",
NSHTTPCookieExpires : [NSDate dateWithTimeIntervalSinceNow:60*60*24]
}];
if (@available(iOS 11.0, *)) {
[web.webView.configuration.websiteDataStore.httpCookieStore setCookie:cookie completionHandler:^{
}];
} else {
// Fallback on earlier versions
}
獲取cookie
if (@available(iOS 11.0, *)) {
[webView.configuration.websiteDataStore.httpCookieStore getAllCookies:^(NSArray<NSHTTPCookie *> * _Nonnull cookies) {
NSLog(@"%@", cookies);
}] ;
} else {
// Fallback on earlier versions
}