轉(zhuǎn)載地址:http://www.reibang.com/p/4fa8c4eb1316
開發(fā)App的過程中腿宰,常常會遇到在App內(nèi)部加載網(wǎng)頁,通常用UIWebView加載吃度。這個自iOS2開始使用的網(wǎng)頁加載器一直是開發(fā)的心病:加載速度慢伊者,占用內(nèi)存多间护,優(yōu)化困難。如果加載網(wǎng)頁多兑牡,還可能因為過量占用內(nèi)存而給系統(tǒng)kill掉。各種優(yōu)化的方法效果也不那么明顯(點擊查看常用優(yōu)化方法)亿虽。
iOS8以后苞也,蘋果推出了新框架Wekkit,提供了替換UIWebView的組件WKWebView如迟。各種UIWebView的問題沒有了殷勘,速度更快了此再,占用內(nèi)存少了玲销,一句話,WKWebView是App內(nèi)部加載網(wǎng)頁的最佳選擇策吠!
先看下WKWebView的特性:
在性能瘩绒、穩(wěn)定性、功能方面有很大提升(最直觀的體現(xiàn)就是加載網(wǎng)頁是占用的內(nèi)存蟀给,模擬器加載百度與開源中國網(wǎng)站時,WKWebView占用23M坤溃,而UIWebView占用85M);
允許JavaScript的Nitro庫加載并使用(UIWebView中限制);
支持了更多的HTML5特性越驻;
高達60fps的滾動刷新率以及內(nèi)置手勢;
將UIWebViewDelegate與UIWebView重構(gòu)成了14類與3個協(xié)議(查看蘋果官方文檔)记劈;
然后從以下幾個方面說下WKWebView的基本用法:
加載網(wǎng)頁
加載的狀態(tài)回調(diào)
新的WKUIDelegate協(xié)議
動態(tài)加載并運行JS代碼
webView 執(zhí)行JS代碼
JS調(diào)用App注冊過的方法
一并巍、加載網(wǎng)頁
加載網(wǎng)頁或HTML代碼的方式與UIWebView相同,代碼示例如下:
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];[webView loadRequest:[NSURLRequestrequestWithURL:[NSURLURLWithString:@"http://www.baidu.com"]]];[self.viewaddSubview:webView];
二刽射、加載的狀態(tài)回調(diào) (WKNavigationDelegate)
用來追蹤加載過程(頁面開始加載剃执、加載完成、加載失斏龅怠)的方法:
// 頁面開始加載時調(diào)用- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation;// 當(dāng)內(nèi)容開始返回時調(diào)用- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation;// 頁面加載完成之后調(diào)用- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation;// 頁面加載失敗時調(diào)用- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation;
頁面跳轉(zhuǎn)的代理方法:
// 接收到服務(wù)器跳轉(zhuǎn)請求之后調(diào)用- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation;// 在收到響應(yīng)后怒见,決定是否跳轉(zhuǎn)- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void(^)(WKNavigationResponsePolicy))decisionHandler;// 在發(fā)送請求之前,決定是否跳轉(zhuǎn)- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void(^)(WKNavigationActionPolicy))decisionHandler;
三闺阱、新的WKUIDelegate協(xié)議
這個協(xié)議主要用于WKWebView處理web界面的三種提示框(警告框配阵、確認框、輸入框)棋傍,下面是警告框的例子:
/** *? web界面中有彈出警告框時調(diào)用 * *@paramwebView? ? ? ? ? 實現(xiàn)該代理的webview *@parammessage? ? ? ? ? 警告框中的內(nèi)容 *@paramframe? ? ? ? ? ? 主窗口 *@paramcompletionHandler 警告框消失調(diào)用 */- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(void(^)())completionHandler;
四、動態(tài)加載并運行JS代碼
用于在客戶端內(nèi)部加入JS代碼亿絮,并執(zhí)行,示例如下:
// 圖片縮放的js代碼NSString*js =@"var count = document.images.length;for (var i = 0; i < count; i++) {var image = document.images[i];image.style.width=320;};window.alert('找到' + count + '張圖');";// 根據(jù)JS字符串初始化WKUserScript對象WKUserScript *script = [[WKUserScript alloc] initWithSource:js injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];// 根據(jù)生成的WKUserScript對象黔姜,初始化WKWebViewConfigurationWKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];[config.userContentControlleraddUserScript:script];_webView = [[WKWebView alloc] initWithFrame:self.view.boundsconfiguration:config];[_webView loadHTMLString:@""baseURL:nil];[self.viewaddSubview:_webView];
五蒂萎、webView 執(zhí)行JS代碼
用戶調(diào)用用JS寫過的代碼,一般指服務(wù)端開發(fā)的:
//javaScriptString是JS方法名五慈,completionHandler是異步回調(diào)block[self.webViewevaluateJavaScript:javaScriptString completionHandler:completionHandler];
六泻拦、JS調(diào)用App注冊過的方法
再WKWebView里面注冊供JS調(diào)用的方法,是通過WKUserContentController類下面的方法:
- (void)addScriptMessageHandler:(id)scriptMessageHandler name:(NSString*)name;
scriptMessageHandler是代理回調(diào)争拐,JS調(diào)用name方法后,OC會調(diào)用scriptMessageHandler指定的對象隘冲。
JS在調(diào)用OC注冊方法的時候要用下面的方式:
window.webkit.messageHandlers..postMessage()
注意音瓷,name(方法名)是放在中間的,messageBody只能是一個對象绳慎,如果要傳多個值,需要封裝成數(shù)組靡砌,或者字典珊楼。整個示例如下:
//OC注冊供JS調(diào)用的方法[[_webView configuration].userContentControlleraddScriptMessageHandler:selfname:@"closeMe"];//OC在JS調(diào)用方法做的處理- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{NSLog(@"JS 調(diào)用了 %@ 方法,傳回參數(shù) %@",message.name,message.body);}//JS調(diào)用window.webkit.messageHandlers.closeMe.postMessage(null);
如果你在self的dealloc打個斷點画舌,會發(fā)現(xiàn)self沒有釋放已慢!這顯然是不行的!谷歌后看到一種解決方法佑惠,如下:
@interfaceWeakScriptMessageDelegate:NSObject@property(nonatomic,weak)id scriptDelegate;- (instancetype)initWithDelegate:(id)scriptDelegate;@end@implementationWeakScriptMessageDelegate- (instancetype)initWithDelegate:(id)scriptDelegate{self= [superinit];if(self) {? ? ? ? _scriptDelegate = scriptDelegate;? ? }returnself;}- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{? ? [self.scriptDelegateuserContentController:userContentController didReceiveScriptMessage:message];}@end
思路是另外創(chuàng)建一個代理對象,然后通過代理對象回調(diào)指定的self旭咽,
WKUserContentController*userContentController = [[WKUserContentControlleralloc]init];? ? [userContentController addScriptMessageHandler:[[WeakScriptMessageDelegatealloc] initWithDelegate:self] name:@"closeMe"];
運行代碼,self釋放了轿塔,WeakScriptMessageDelegate卻沒有釋放啊啊扒攵狻!
還需在self的dealloc里面 添加這樣一句代碼:
[[_webView configuration].userContentControllerremoveScriptMessageHandlerForName:@"closeMe"];
OK宗收,圓滿解決問題亚兄!
目前,大多數(shù)App需要支持iOS7以上的版本匈勋,而WKWebView只在iOS8后才能用膳叨,所以需要一個兼容性方案,既iOS7下用UIWebView菲嘴,iOS8后用WKWebView。這個庫提供了這種兼容性方案:https://github.com/wangyangcc/IMYWebView
以上部分內(nèi)容參考自:http://www.brighttj.com/ios/ios-wkwebview-new-features-and-use.html
文/wangyangyang(簡書作者)
原文鏈接:http://www.reibang.com/p/6ba2507445e4
著作權(quán)歸作者所有昭雌,轉(zhuǎn)載請聯(lián)系作者獲得授權(quán)健田,并標(biāo)注“簡書作者”。