UIWebView
UIWebView是蘋果繼承於UIView封裝的一個加載web內容的類,它可以加載任何遠端的web數據展示在你的頁面上,你可以像浏覽器一樣前進後退刷新等操作。不過蘋果在iOS8以後推出了WKWebView來加載Web,下面再詳細介紹下WKWebView。
UIWebView屬於UIKit,封裝了WebKit.framework的WebView.
WebView組合管理了WebCore.framework的Page,並提供了各種Clients.
Page管理了Main Frame,Main Frame管理了sub Frame(FrameTree)(關於詳細的UIWebView介紹轉自這裡)
WebView繼承自WAKView,WAKView類似於NSView,可以做較少的改動使得Mac和iOS共用一套。由UIWebDocumentView對WebView進行操作並接收回調事件,當數據發生變化的時候,就會通知UIWebTiledView重新繪制。
UIWebTiledView和WAKWindow這兩個類主要負責頁面的繪制,包括布局繪圖排版,交互等,WAKWindow還會做一些用戶操作事件的分派。
UIWebBrowserView主要負責:
* form的自動填充
* fixed元素的位置調整
* JavaScript的手勢識別
* 鍵盤彈出時的視圖滾動處理,防止遮擋
* 提供接口讓UIWebView獲取信息
* 為顯示PDF時添加頁號標簽
通過反編譯可以獲得UIWebViewInternal的具體成員變量
@interface UIWebViewInternal : NSObject { UIScrollView *scroller; UIWebBrowserView *browserView; UICheckeredPatternView *checkeredPatternView; iddelegate; unsigned int scalesPageToFit; unsigned int isLoading; unsigned int hasOverriddenOrientationChangeEventHandling; unsigned int drawsCheckeredPattern; unsigned int webSelectionEnabled; unsigned int drawInWebThread; unsigned int inRotation; NSURLRequest *request; int clickedAlertButtonIndex; UIWebViewWebViewDelegate *webViewDelegate; UIWebPDFViewHandler *pdfHandler; } @end
由此可以看出UIWebViewInternal是接收WebView的事件的載體通過自身把WebView的事件傳遞給UIWebView.
WKWebView
通過上面的了解,蘋果終於在8.0之後開放了WKWebView應用於iOS和OSX中,它取代了UIWebView和WebView,在兩個平台上支持同一套API。
它脫離於UIWebView的設計,將原本的設計拆分成14個類,和3個代理協議,雖然是這樣但是了解之後其實用法比較簡單,依照職責單一的原則,每個協議做的事情根據功能分類。
WKWebView相比於UIWebView
* WKWebView的內存遠遠沒有UIWebView的開銷大,而且沒有緩存
* 擁有高達60FPS滾動刷新率及內置手勢
* 支持了更多的HTML5特性
* 高效的app和web信息交換通道
* 允許JavaScript的Nitro庫加載並使用,UIWebView中限制了
* WKWebView目前缺少關於頁碼相關的API
* 提供加載網頁進度的屬性
WKWebView的協議
WKScriptMessageHandler協議
window.webkit.messageHandlers.{NAME}.postMessage()
可以把JavaScript對象通過該API自動轉換成Objective-C或Swift 對象,Name可以通過addScriptMessageHandler: name:來設置
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{ }
作為唯一響應JavaScript的協議方法,目的是為了與其它的進行分離,在該協議中響應之前注入的MessageHandlers.
可以根據WKScriptMessage知道Js的名稱和參數,來區分不同的響應事件
WKNavigationDelegate協議
提供了追蹤主窗口網頁加載過程和判斷主窗口和子窗口是否進行頁面加載新頁面的相關方法,相當於UIWebView中webViewDidFinishLoad和webViewDidStartLoad方法,除了有開始加載、加載成功、加載失敗的API外,還具有額外的三個代理方法:
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation; - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler; - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
第一個是服務器redirect時調用
第二個API是根據客戶端受到的服務器響應頭以及response相關信息來決定是否可以跳轉
第三個API是根據WebView對於即將跳轉的HTTP請求頭信息和相關信息來決定是否跳轉
WKUIDelegate協議
提供用原生控件顯示網頁的方法回調,例如Alert提示可以自定義用原生的控件來實現
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler { UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:message preferredStyle:UIAlertControllerStyleAlert]; [alert addAction:[UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { completionHandler(); }]]; [self presentViewController:alert animated:YES completion:NULL]; }
總結
WKWebView相較於UIWebView在整體上有較大的提升,滿足OS上面使用同一套控件的功能,同時對整個內存的開銷以及滾動刷新率和JS交互做了優化的處理。依據職責單一的原則,拆分成了三個協議去實現WebView的響應,解耦了JS交互和加載進度的響應處理。WKWebView沒有做緩存處理,所以對網頁需要緩存的加載性能要求沒那麼高的還是可以考慮UIWebView.
結合以上這些,最近封裝一個基礎的WebView控件[CHWebView](https://github.com/chausson/CHWebView),目前並沒有做成一個通用控件,反而是封裝帶有UIWebView和WKWebView的基類,以後有時間的話把WebView這一層分離
Demo下載:https://github.com/chausson/CHWebView