前言
Xcode8發(fā)布以后,編譯器開始不支持IOS7东揣,所以很多應(yīng)用在適配IOS10之后都不在適配IOS7了践惑,其中包括了很多大公司,網(wǎng)易新聞嘶卧,滴滴出行等尔觉。因此,我們公司的應(yīng)用也打算淘汰IOS7芥吟。
支持到IOS8侦铜,第一個(gè)要改的自然是用WKWebView替換原來的UIWebView。WKWebView有很多明顯優(yōu)勢:
更多的支持HTML5的特性
官方宣稱的高達(dá)60fps的滾動(dòng)刷新率以及內(nèi)置手勢
將UIWebViewDelegate與UIWebView拆分成了14類與3個(gè)協(xié)議,以前很多不方便實(shí)現(xiàn)的功能得以實(shí)現(xiàn)运沦。文檔
Safari相同的JavaScript引擎
占用更少的內(nèi)存
UIWebView
functionsay(){//前端需要用 window.webkit.messageHandlers.注冊的方法名.postMessage({body:傳輸?shù)臄?shù)據(jù)} 來給native發(fā)送消息window.webkit.messageHandlers.sayhello.postMessage({body:'hello world!'});}
UIWebView
WKWebView
WKWebView
因此泵额,使用WkWebview替換UIWebView還是很有必要的配深。
WKWebView有兩個(gè)delegate,WKUIDelegate和WKNavigationDelegate携添。WKNavigationDelegate主要處理一些跳轉(zhuǎn)、加載處理操作篓叶,WKUIDelegate主要處理JS腳本烈掠,確認(rèn)框,警告框等缸托。因此WKNavigationDelegate更加常用左敌。
比較常用的方法:
#pragma mark - lifeCircle
- (void)viewDidLoad {? ? [superviewDidLoad];? ? webView = [[WKWebView alloc]init];? ? [self.viewaddSubview:webView];? ? [webView mas_makeConstraints:^(MASConstraintMaker *make) {? ? ? ? make.left.equalTo(self.view);? ? ? ? make.right.equalTo(self.view);? ? ? ? make.top.equalTo(self.view);? ? ? ? make.bottom.equalTo(self.view);? ? }];? ? webView.UIDelegate=self;? ? webView.navigationDelegate=self;? ? [webView loadRequest:[NSURLRequestrequestWithURL:[NSURLURLWithString:@"http://www.baidu.com"]]];}
#pragma mark - WKNavigationDelegate
// 頁面開始加載時(shí)調(diào)用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{}
// 當(dāng)內(nèi)容開始返回時(shí)調(diào)用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation{}
// 頁面加載完成之后調(diào)用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{}
// 頁面加載失敗時(shí)調(diào)用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation{}
// 接收到服務(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{NSLog(@"%@",navigationResponse.response.URL.absoluteString);//允許跳轉(zhuǎn)decisionHandler(WKNavigationResponsePolicyAllow);//不允許跳轉(zhuǎn)//decisionHandler(WKNavigationResponsePolicyCancel);}
// 在發(fā)送請求之前俐镐,決定是否跳轉(zhuǎn)
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void(^)(WKNavigationActionPolicy))decisionHandler{NSLog(@"%@",navigationAction.request.URL.absoluteString);//允許跳轉(zhuǎn)decisionHandler(WKNavigationActionPolicyAllow);//不允許跳轉(zhuǎn)//decisionHandler(WKNavigationActionPolicyCancel);}
#pragma mark - WKUIDelegate
// 創(chuàng)建一個(gè)新的WebView
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures{return[[WKWebView alloc]init];}
// 輸入框
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString*)prompt defaultText:(nullableNSString*)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void(^)(NSString* __nullable result))completionHandler{? ? completionHandler(@"http");}
// 確認(rèn)框
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString*)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void(^)(BOOLresult))completionHandler{? ? completionHandler(YES);}
// 警告框
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString*)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void(^)(void))completionHandler{NSLog(@"%@",message);? ? completionHandler();}
WKWebview提供了API實(shí)現(xiàn)js交互 不需要借助JavaScriptCore或者webJavaScriptBridge矫限。使用WKUserContentController實(shí)現(xiàn)js native交互。簡單的說就是先注冊約定好的方法,然后再調(diào)用叼风。
oc代碼(有誤取董,內(nèi)存不釋放):
@interfaceViewController(){? ? WKWebView * webView;? ? WKUserContentController* userContentController;}@end
@implementationViewController
#pragma mark - lifeCircle
- (void)viewDidLoad {? ? [superviewDidLoad];//配置環(huán)境WKWebViewConfiguration * configuration = [[WKWebViewConfiguration alloc]init];? ? userContentController =[[WKUserContentController alloc]init];? ? configuration.userContentController= userContentController;? ? webView = [[WKWebView alloc]initWithFrame:CGRectMake(0,0,100,100) configuration:configuration];//注冊方法[userContentController addScriptMessageHandler:selfname:@"sayhello"];//注冊一個(gè)name為sayhello的js方法[self.viewaddSubview:webView];? ? [webView mas_makeConstraints:^(MASConstraintMaker *make) {? ? ? ? make.left.equalTo(self.view);? ? ? ? make.right.equalTo(self.view);? ? ? ? make.top.equalTo(self.view);? ? ? ? make.bottom.equalTo(self.view);? ? }];? ? webView.UIDelegate=self;? ? webView.navigationDelegate=self;? ? [webView loadRequest:[NSURLRequestrequestWithURL:[NSURLURLWithString:@"http://www.test.com"]]];}
- (void)dealloc{//這里需要注意,前面增加過的方法一定要remove掉无宿。[userContentController removeScriptMessageHandlerForName:@"sayhello"];}
#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{NSLog(@"name:%@\\\\n body:%@\\\\n frameInfo:%@\\\\n",message.name,message.body,message.frameInfo);}
@end
上面的OC代碼如果認(rèn)證測試一下就會(huì)發(fā)現(xiàn)dealloc并不會(huì)執(zhí)行茵汰,這樣肯定是不行的,會(huì)造成內(nèi)存泄漏孽鸡。原因是[userContentController addScriptMessageHandler:self name:@"sayhello"];這句代碼造成無法釋放內(nèi)存蹂午。(ps:試了下用weak指針還是不能釋放,不知道是什么原因彬碱。)因此還需要進(jìn)一步改進(jìn)豆胸,正確的寫法是用一個(gè)新的controller來處理,新的controller再繞用delegate繞回來。
functionsay(){//前端需要用 window.webkit.messageHandlers.注冊的方法名.postMessage({body:傳輸?shù)臄?shù)據(jù)} 來給native發(fā)送消息window.webkit.messageHandlers.sayhello.postMessage({body:'hello world!'});}
hello world
say hello
打印出的log:
name:sayhello body:{? ? body ="hello world!";} frameInfo: { URL: http://www.test.com/ }>
addScriptMessageHandler要和removeScriptMessageHandlerForName配套出現(xiàn)堡妒,否則會(huì)造成內(nèi)存泄漏配乱。
h5只能傳一個(gè)參數(shù),如果需要多個(gè)參數(shù)就需要用字典或者json組裝皮迟。
代碼如下:
- (void)webView:(WKWebView *)tmpWebView didFinishNavigation:(WKNavigation *)navigation{//say()是JS方法名搬泥,completionHandler是異步回調(diào)block[webView evaluateJavaScript:@"say()"completionHandler:^(id_Nullable result,NSError* _Nullable error) {NSLog(@"%@",result);? ? }];}
h5代碼同上。
一般來說伏尼,一個(gè)好的UI總有一個(gè)大神會(huì)開發(fā)出一個(gè)好的第三方封裝框架忿檩。WebViewJavascriptBridge的作者也做了一套支持WKWebView與JS交互的第三方框架:WKWebViewJavascriptBridge。
cocoaPods:pod 'WebViewJavascriptBridge', '~> 5.0.5'
github地址:https://github.com/marcuswestin/WebViewJavascriptBridge
主要方法如下:
//初始化方法
+ (instancetype)bridgeForWebView:(WKWebView*)webView;
+ (void)enableLogging;
//注冊函數(shù)名
- (void)registerHandler:(NSString*)handlerName handler:(WVJBHandler)handler;
//調(diào)用函數(shù)名
- (void)callHandler:(NSString*)handlerName;
- (void)callHandler:(NSString*)handlerName data:(id)data;
- (void)callHandler:(NSString*)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback;
//重置
- (void)reset;
//設(shè)置WKNavigationDelegate
- (void)setWebViewDelegate:(id)webViewDelegate;