大神總結WKWebView的坑:https://mp.weixin.qq.com/s/rhYKLIbXOsUJC_n6dt9UfAhtml
在開發過程當中,常常會出現須要iOS移動端與H5混編的使用場景。 iOS中加載html網頁, 可使用UIWebView或WKWebView. 本篇博客將介紹兩種控件使用過程當中如何實現OC與JS的交互。
前端
UIWebView delegate 協議方法 //網頁即將開始加載 - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType; //網頁開始加載 - (void)webViewDidStartLoad:(UIWebView *)webView; //網頁加載完成 - (void)webViewDidFinishLoad:(UIWebView *)webView; //網頁加載失敗 - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error; //UIWebView自帶了一個方法, 能夠直接調用JS代碼(轉化爲string類型的js代碼) - (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script; //例如修改id爲‘html’標籤內部的text屬性 [web stringByEvaluatingJavaScriptFromString:@"document.getElementById('html').innerText='修改內容'"]; //也能夠執行多行js代碼 [web stringByEvaluatingJavaScriptFromString:@"var div = document.getElementById('html'); div.innerText = '修改內容'"];
利用JavaScriptCore實現交互
JavaScriptCore中類及協議:java
JSContext:給JavaScript提供運行的上下文環境 JSValue:JavaScript和Objective-C數據和方法的橋樑 JSManagedValue:管理數據和方法的類 JSVirtualMachine:處理線程相關,使用較少 JSExport:這是一個協議,若是採用協議的方法交互,本身定義的協議必須遵照此協議OC中提供了JavaScriptCore 這個庫,使得OC與js的交互變得更加方便。 使用方法: 1 加入JavaScriptCore 這個framework 2 引入頭文件<JavaScriptCore/JavaScriptCore.h> 3 在VC裏面加入一個JSContext屬性 @property (strong, nonatomic) JSContext *context; JSContext是什麼那? 咱們看一下api裏面的解釋
@interface @discussion A JSContext is a JavaScript execution environment. All JavaScript execution takes place within a context, and all JavaScript values are tied to a context.大概意思是說:JSContext是一個JS的執行環境,全部的JS執行都發生在一個context裏面, 全部的JS value都綁定到context裏面 具體使用
//初始化context self.context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; //OC調用JS //(1)例如html的script中一個方法 function dolike(a,b,c){ } //經過OC調用此方法 NSString * method = @"dolike"; JSValue * function = [self.context objectForKeyedSubscript:method]; //這裏面的a,b,c就是OC調用JS的時候給JS傳的參數 [function callWithArguments:@[a,b,c]]; //JS調用OC //例如網頁中有個標籤,點擊button的時候調用Jump方法, 此處3爲傳入的參數 <button onclick="jump('3')">點我</button> //當點擊網頁中的button的時候,觸發jump方法, 在OC中用以下代碼能夠捕捉到jump方法, 並拿到JS給我傳的參數‘3’ self.context[@"jump"] = ^(NSString * str){ //此處 str 值爲'3'(js調用OC時傳給OC的參數) };
WKWebView :
說到WKWebView, 首先要說下WKWebView的優點web
1 更多的支持HTML5的特性
2 官方宣稱的高達60fps的滾動刷新率以及內置手勢
3 將UIWebViewDelegate與UIWebView拆分紅了14類與3個協議,之前不少不方便實現 的功能得以實現
4 Safari相同的JavaScript引擎
5 佔用更少的內存json
類:api
WKBackForwardList: 以前訪問過的 web 頁面的列表,能夠經過後退和前進動做來訪問到。
WKBackForwardListItem: webview 中後退列表裏的某一個網頁。
WKFrameInfo: 包含一個網頁的佈局信息。
WKNavigation: 包含一個網頁的加載進度信息。
WKNavigationAction: 包含可能讓網頁導航變化的信息,用於判斷是否作出導航變化。
WKNavigationResponse: 包含可能讓網頁導航變化的返回內容信息,用於判斷是否作出導航變化。
WKPreferences: 歸納一個 webview 的偏好設置。
WKProcessPool: 表示一個 web 內容加載池。
WKUserContentController: 提供使用 JavaScript post 信息和注射 script 的方法。
WKScriptMessage: 包含網頁發出的信息。
WKUserScript: 表示能夠被網頁接受的用戶腳本。
WKWebViewConfiguration: 初始化 webview 的設置。
WKWindowFeatures: 指定加載新網頁時的窗口屬性。
協議:服務器
WKNavigationDelegate: 提供了追蹤主窗口網頁加載過程和判斷主窗口和子窗口是否進行頁面加載新頁面的相關方法。
WKScriptMessageHandler: 提供從網頁中收消息的回調方法。
WKUIDelegate: 提供用原生控件顯示網頁的方法回調。
加載方式: //方式一 WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds]; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]]]; [self.view addSubview:webView]; //方式二 WKWebViewConfiguration * configuration = [[WKWebViewConfiguration alloc] init]; webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration]; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]]]; [self.view addSubview:webView]; 協議方法介紹: #pragma mark - WKNavigationDelegate // 頁面開始加載時調用 - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{ } // 當內容開始返回時調用 - (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation{ } // 頁面加載完成以後調用 - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{ } // 頁面加載失敗時調用 - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation{ } // 接收到服務器跳轉請求以後調用 - (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation{ } // 在收到響應後,決定是否跳轉 - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{ NSLog(@"%@",navigationResponse.response.URL.absoluteString); //容許跳轉 decisionHandler(WKNavigationResponsePolicyAllow); //不容許跳轉 //decisionHandler(WKNavigationResponsePolicyCancel); } // 在發送請求以前,決定是否跳轉 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{ NSLog(@"%@",navigationAction.request.URL.absoluteString); //容許跳轉 decisionHandler(WKNavigationActionPolicyAllow); //不容許跳轉 decisionHandler(WKNavigationActionPolicyCancel); } #pragma mark - WKUIDelegate // 建立一個新的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:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler{ completionHandler(@"http"); } // 確認框 - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler{ completionHandler(YES); } // 警告框 - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{ NSLog(@"%@",message); completionHandler(); }
OC與JS的交互ide
WKWebView佈局
WKWebView的 UIDelegate 提供了三個協議方法, 可讓前端很方便的攔截JS的alert, confirm, prompt方法。除此以外,OC,JS互調能夠按照以下方法。post
1 OC 調用JS
可使用webkit這個庫
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler; //例如OC調用JS的方法 setName [webView evaluateJavaScript:@"setname('張三')" completionHandler:nil]; //此處 setname爲JS定義的方法名, 內部 ‘張三’爲傳給JS的參數。 若是setname方法須要傳入一個json或者array等非字符參數, 須要用format方法將其轉爲string類型,在調用evaluate方法。例如 NSString * para = [NSString stringWithFormat:@"setname('%@')",json];
JS調用OC
此時就要用到WKScriptMessageHandler了
//首先.m中加入屬性 @property (nonatomic ,strong)WKUserContentController * userCC; //1 遵循WKScriptMessageHandler協議 //2 初始化 WKWebViewConfiguration * config = [[WKWebViewConfiguration alloc]init]; self.wkWebViw = [[WKWebView alloc]initWithFrame:self.view.bounds configuration:config]; [self.wkWebViw loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.webPageUrl]]]; [self.view addSubview:self.wkWebViw]; self.userCC = config.userContentController; [self.userCC addScriptMessageHandler:self name:@"callOSX"]; //此處至關於監聽了JS中callFunction這個方法 [self.userCC addScriptMessageHandler:self name:@"callFunction"]; //當JS發出callFunction這個方法指令的時候, WKScriptMessageHandler的協議方法中咱們就會收到這個消息 #pragma mark WKScriptMessageHandler delegate - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { //這個回調裏面, message.name表明方法名(‘本例爲 callFunction’), message.body表明JS給咱們傳過來的參數 } //最後, VC銷燬的時候必定要把handler移除 -(void)dealloc { [_userContentController removeScriptMessageHandlerForName:@"callFunction"]; } //對應的JS代碼 <button onclick="buttonClick('舒適提示')">點我</button> <script> function buttonClick(string){ //JS調用OC, 格式以下 //(window.webkit.messageHandlers.Method_Name.postMessage(parameterToOC)) window.webkit.messageHandlers.callFunction.postMessage(string) } </script>