WKWebView 是 iOS 8.0 以后用于替代 UIWebView 的瀏覽器組件蚁阳。和 UIWebView 相比,具有渲染性能更好鸽照、支持更多的 HTML5 特性韵吨、控制更加細致等諸多優(yōu)點。
一直沒沉下心來學習移宅,我們一起好好看看里面都有什么吧??
WKWebView
主要的
@interface WKWebView : UIView
//重要屬性
@property (nonatomic, readonly, copy) WKWebViewConfiguration *configuration;
@property (nullable, nonatomic, weak) id <WKNavigationDelegate> navigationDelegate;
@property (nullable, nonatomic, weak) id <WKUIDelegate> UIDelegate;
@property (nonatomic, readonly, strong) WKBackForwardList *backForwardList;
@property (nonatomic, readonly, nullable) SecTrustRef serverTrust;
@property (nullable, nonatomic, copy) NSString *customUserAgent;
@property (nonatomic) BOOL allowsLinkPreview;
@property (nonatomic, readonly, strong) UIScrollView *scrollView;
...
//加載方法
- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;
- (nullable WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL;
...
- (nullable WKNavigation *)goBack;
- (nullable WKNavigation *)goForward;
- (nullable WKNavigation *)reload;
- (nullable WKNavigation *)reloadFromOrigin;
//類方法
+ (BOOL)handlesURLScheme:(NSString *)urlScheme;
//與JS交互接口
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;
//iOS 14新增的API
- (void)evaluateJavaScript:(NSString *)javaScriptString inFrame:(nullable WKFrameInfo *)frame inContentWorld:(WKContentWorld *)contentWorld completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;
- (void)callAsyncJavaScript:(NSString *)functionBody arguments:(nullable NSDictionary<NSString *, id> *)arguments inFrame:(nullable WKFrameInfo *)frame inContentWorld:(WKContentWorld *)contentWorld completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;
@end
WKWebView 初始化
一個簡單用于展示的 WebView 可以是這樣的:
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
WKWebView *webView = ({
webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
webView.scrollView.bounces = NO;
webView.backgroundColor = [UIColor whiteColor];
webView;
});
[self.view addSubview:webView];
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://baidu.com"]]];
這樣一個單純用于展示網(wǎng)頁归粉、沒有任何配置項的 WebView 就算完成了。相較于 UIWebView
漏峰,我們看到多了一個 configuration
配置類型糠悼,那我們就來 Read the fucking source code…...
WKWebViewConfiguration
官方文檔是這樣描述這個類型的:
A
WKWebViewConfiguration
object is a collection of properties with which to initialize a web view.一個用于初始化 web view 屬性的集合。
我們可以用它做什么呢浅乔?
- 設置用于網(wǎng)站的初始cookie
- 處理自定義的 URL schemes
- 設置如何處理媒體內容
- 管理網(wǎng)頁中選中的信息
- 自定義注入網(wǎng)頁的腳本
- 自定義內容的展示規(guī)則
- …...
我們可以通過創(chuàng)建一個 WKWebViewConfiguration
對象來設置網(wǎng)頁的屬性倔喂,并且在 WebView 初始化的時候傳遞給它。注意的是只能在初始化的時候配置 configuration
中的屬性靖苇,后面是沒辦法動態(tài)再去修改這些配置的席噩。
我們通過這個類看看 WebKit 里面有哪些主要的內容:
WKProcessPool
/*! @abstract The process pool from which to obtain the view's web content
process.
@discussion When a web view is initialized, a new web content process
will be created for it from the specified pool, or an existing process in
that pool will be used.
*/
@property (nonatomic, strong) WKProcessPool *processPool;
@interface WKProcessPool : NSObject <NSSecureCoding>
@end
官方文檔解釋為一個可以在單個進程中運行多個 web 視圖的不可見 token?令牌贤壁?悼枢。進程池∑⒉穑可以看到 WKProcessPool 類沒有暴漏任何接口馒索,這意味著我們只能創(chuàng)建和讀取該對象莹妒,通過對象地址判斷是否在相同進程。
WKWebView 為了安全和穩(wěn)定性考慮绰上,會為每一個 WKWebView 實例分配獨立的進程(而不是直接使用APP的進程空間)旨怠,系統(tǒng)會有一個設定的進程個數(shù)上線。相同 WKProcessPool 對象的 WKWebView 共享相同的進程空間蜈块。這也是與 UIWebView
最大不同的一點:NSHTTPCookieStorage
中的 cookie 鉴腻,UIWebView
是可以自動攜帶使用的,但 WKWebView
無法獲取 Storage中 的 cookie百揭。
誒拘哨,那是不是放在同一個進程池中的 web view 就可以共享 cookie 了呢?帶著這個問題信峻,稍后我們會提到 cookie 有關的處理倦青。
WKPreferences
*/
@property (nonatomic, strong) WKPreferences *preferences;
針對 web 視圖的偏好設置,如果是針對 web 內容的設置還是使用 WKWebViewConfiguration
盹舞,感覺這個類還在完善产镐、擴充中,內容很少踢步。比較值得注意的是與 JavaScript 有關的兩個屬性癣亚。
//字體
@property (nonatomic) CGFloat minimumFontSize;
//是否允許在沒有用戶交互的情況下,JavaScript可以打開windows
@property (nonatomic) BOOL javaScriptCanOpenWindowsAutomatically;
//是否啟用javaScript获印,14.0 以后就廢棄了述雾,有對應替換的 API
@property (nonatomic) BOOL javaScriptEnabled;//ios(8.0, 14.0)
//是否提醒 如網(wǎng)絡釣魚或惡意軟件 等可疑的欺詐內容
@property (nonatomic) BOOL fraudulentWebsiteWarningEnabled;//ios(13.0)
WKUserContentController
/*! @abstract The user content controller to associate with the web view.
*/
@property (nonatomic, strong) WKUserContentController *userContentController;
這個類提供了一個 JavaScript 向 web view 發(fā)送消息的途徑,可以增刪用戶腳本兼丰。
JavaScript 與原生做交互玻孟,比較多的一個場景是需要調用原生的某些能力。在 UIWebView
中簡單的方式是攔截請求鳍征,根據(jù)特定的 scheme 或者參數(shù)來區(qū)分黍翎,那在 WKWebView
中則是通過 WKUserContentController
添加消息處理器。例如打開相機功能:
- 添加腳本處理器
WKUserContentController *userContentController = [[WKUserContentController alloc] init];
[userContentController addScriptMessageHandler:self name:@"OpenCamera"];
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
configuration.userContentController = userContentController;
- 實現(xiàn) WKScriptMessageHandler 代理的方法
#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
if ([message.name caseInsensitiveCompare:@"OpenCamera"] == NSOrderedSame) {
//Call your open camera action.
}
}
- JS 中調用
window.webkit.messageHandlers.OpenCamera.postMessage(/*需要傳的參數(shù)艳丛,兩端約定好*/);
我們看到接收 JavaScript 的消息是通過一個 WKScriptMessage
類型匣掸,我們再來簡單看下這個類
WKScriptMessage
A WKScriptMessage object contains information about a message sent from a webpage.
他的注釋就很簡單了:一個包含網(wǎng)頁發(fā)來的消息內容的對象。
// 消息體氮双、參數(shù)碰酝。允許的類型 NSNumber, NSString, NSDate, NSArray, NSDictionary, and NSNull.
@property (nonatomic, readonly, copy) id body;
// 發(fā)送消息的 web view。
@property (nullable, nonatomic, readonly, weak) WKWebView *webView;
// 前端中發(fā)送消息的 frame戴差。
@property (nonatomic, readonly, copy) WKFrameInfo *frameInfo;
// 用于接收前端消息的處理器的名字送爸。
@property (nonatomic, readonly, copy) NSString *name;
// The content world from which the message was sent. ?我也還沒用過不清楚是干嘛的。
@property (nonatomic, readonly) WKContentWorld *world API_AVAILABLE(macos(11.0), ios(14.0));
WKUserScript
A script that the web view injects into a webpage.
當需要將自定義腳本代碼注入 Web 頁面時碱璃,可以創(chuàng)建一個 WKUserScript 對象弄痹。使用此對象指定要注入的 JavaScript 代碼饭入,以及與注入該代碼的時間和方式相關的參數(shù)嵌器。通過前面提到的 WKUserContentController 調用 addUserScript:
完成注入。
@interface WKUserScript : NSObject <NSCopying>
//JS 代碼
@property (nonatomic, readonly, copy) NSString *source;
//JS 注入的時機
@property (nonatomic, readonly) WKUserScriptInjectionTime injectionTime;
//是否只用于主視圖
@property (nonatomic, readonly, getter=isForMainFrameOnly) BOOL forMainFrameOnly;
//初始化
- (instancetype)initWithSource:(NSString *)source injectionTime:(WKUserScriptInjectionTime)injectionTime forMainFrameOnly:(BOOL)forMainFrameOnly;
//iOS14 新增的一個方法谐丢,區(qū)分JS的“作用域”
- (instancetype)initWithSource:(NSString *)source injectionTime:(WKUserScriptInjectionTime)injectionTime forMainFrameOnly:(BOOL)forMainFrameOnly inContentWorld:(WKContentWorld *)contentWorld WK_API_AVAILABLE(macos(11.0), ios(14.0));
@end
WKContentWorld
An object that defines a scope of execution for JavaScript code, and which you use to prevent conflicts between different scripts.
WKContentWorld 是 iOS 14 的新增內容爽航,可以理解為不同的命名空間、不同的運行環(huán)境乾忱。顯而易見的讥珍,在邏輯上,原生的 JS 環(huán)境和 web JS 運行環(huán)境存在命名沖突的可能窄瘟。WKContentWorld 有兩個類屬性 defaultClientWorld
衷佃、pageWorld
,分別代表原生和 web 容器的 JS 運行空間蹄葱。開發(fā)者也可以通過:
+ (WKContentWorld *)worldWithName:(NSString *)name;
工廠方法創(chuàng)建一個獨立的 JS 運行環(huán)境氏义。
WKWebsiteDataStore
這個類貌似包含了一個 web view 的所有數(shù)據(jù),我看完這個類的介紹图云,第一感覺是惯悠,哇,我可以窺探一切了竣况。然而克婶,除了 cookie,一毛錢都拿不到……不講了丹泉,有興趣自己試吧~~
(
WKWebsiteDataTypeDiskCache,
WKWebsiteDataTypeOfflineWebApplicationCache,
WKWebsiteDataTypeMemoryCache,
WKWebsiteDataTypeLocalStorage,
WKWebsiteDataTypeFetchCache,
WKWebsiteDataTypeCookies,
WKWebsiteDataTypeSessionStorage,
WKWebsiteDataTypeIndexedDBDatabases,
WKWebsiteDataTypeWebSQLDatabases,
WKWebsiteDataTypeServiceWorkerRegistrations
)
WKHTTPCookieStore
A WKHTTPCookieStore object allows managing the HTTP cookies associated with a particular WKWebsiteDataStore.
用來管理與特定
WKWebsiteDataStore
相關聯(lián)的 HTTP cookie情萤。
API 看上去很簡單…但獲得 cookie 是異步操作,與 NSHTTPCookieStorage
的同步操作不同摹恨,處理起來可能要注意下紫岩。
- (void)getAllCookies:(void (^)(NSArray<NSHTTPCookie *> *))completionHandler;
- (void)setCookie:(NSHTTPCookie *)cookie completionHandler:(nullable void (^)(void))completionHandler;
- (void)deleteCookie:(NSHTTPCookie *)cookie completionHandler:(nullable void (^)(void))completionHandler;
- (void)addObserver:(id<WKHTTPCookieStoreObserver>)observer;
- (void)removeObserver:(id<WKHTTPCookieStoreObserver>)observer;
對應觀察者的協(xié)議方法:
在 cookie 發(fā)生變化時,可以異步通知睬塌,但經(jīng)測試是有一點延遲的泉蝌,有興趣可以測一測?
@protocol WKHTTPCookieStoreObserver <NSObject>
@optional
- (void)cookiesDidChangeInCookieStore:(WKHTTPCookieStore *)cookieStore;
@end
WKBackForwardList
訪問過的 web 頁面歷史記錄揩晴。
WKNavigation
WKNavigation 對象可以用來了解網(wǎng)頁的加載進度勋陪。通過 loadRequest、goBack 等方法加載頁面時硫兰,將返回一個 WKNavigation 對象诅愚。通過 WKNavigationDelegate 代理的以下幾個方法,可以知道頁面的加載情況。WKNavigationDelegate 稍后我們再說违孝。
WKNavigationAction
包含網(wǎng)頁導航信息刹前,需要據(jù)此顯示對應的操作界面。
WKFrameInfo
標識當前網(wǎng)頁內容信息的對象雌桑。