在iOS應用的開發過程當中,咱們常常會使用到WebView,當咱們對WebView進行操做的時候,有時會須要進行源生的操做.那麼我記下來就與你們分享一下OC與JS交互.html
首先先說第一種方法,並無牽扯OC與JS交互,只是作攔截和跳轉.前端
//UIWebViewDelegate - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { NSString *url = request.URL.absoluteString; if ([url rangeOfString:@"須要跳轉源生界面的URL判斷"].location != NSNotFound) { //跳轉原生界面 return NO; } return YES; }
//使用WKWebview須要導入WebKit #import <WebKit/WebKit.h> //WKNavigationDelegate - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler { NSString *url = navigationAction.request.URL.absoluteString; if ([url rangeOfString:@"須要跳轉源生界面的URL判斷"].location != NSNotFound) { //跳轉原生界面 //Cancel the navigation decisionHandler(WKNavigationActionPolicyCancel); return; } decisionHandler(WKNavigationActionPolicyAllow); }
正入主題.java
①直接運行git
NSString *jsStr = @"執行的JS代碼"; [webView stringByEvaluatingJavaScriptFromString:jsStr];
②使用JavaScriptCore框架github
#import <JavaScriptCore/JavaScriptCore.h> - (void)webViewDidFinishLoad:(UIWebView *)webView { //獲取webview中的JS內容 JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; NSString *runJS = @"執行的JS代碼"; //準備執行的JS代碼 [context evaluateScript:runJS]; }
[webView evaluateJavaScript:@"執行的JS代碼" completionHandler:^(id _Nullable response, NSError * _Nullable error) { }];
當網頁觸發某種操做,能夠給App傳遞消息.好比WebView中購買某樣東西,點擊購買,須要獲取這件商品的訂單信息,而且須要App進行源生的支付.
這種方法須要你和後臺或者前端協商好一下,讓他們在執行JS方法的時候,將你須要的數據放到你能拿到的位置.web
下面簡單貼一個HTML文件.json
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>App與WebView交互</title> </head> <body> <button style="width: 100%; height: 100px;" onclick="buttonClick()">點擊購買</button> </body> <script> //按鈕點擊事件 function buttonClick() { //傳遞的信息 var jsonStr = '{"id":"666", "message":"我是傳遞的數據"}'; //UIWebView使用 getMessage(jsonStr); //WKWebView使用 //使用下方方法,會報錯,爲使界面執行邏輯通暢,所以使用try-catch try { window.webkit.messageHandlers.getMessage.postMessage(jsonStr) } catch(error) { console.log(error) } } function getMessage(json){ //空方法 } </script> </html>
window.webkit.messageHandlers.<方法名>.postMessage(<數據>) JS端寫此方法的盆友可能會報錯,致使界面邏輯沒法進行,所以使用try-catch就行了.
我在網頁上只寫了一個按鈕.點擊按鈕,會觸發buttonClick()
方法.服務器
在網頁加載完成的時候檢測JS方法執行.框架
- (void)webViewDidFinishLoad:(UIWebView *)webView { //核心方法以下 JSContext *content = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; //此處的getMessage和JS方法中的getMessage名稱一致. content[@"getMessage"] = ^() { NSArray *arguments = [JSContext currentArguments]; for (JSValue *jsValue in arguments) { NSLog(@"=======%@",jsValue); } }; }
由上方方法,當JS方法getMessage()
執行的時候,此方法回調的jsValue內容就是咱們須要的內容.(HTML中JS傳遞的數據)ide
WebView中的getMessage與HTML文件JS方法的getMessage名稱需保持一致.
實現WKScriptMessageHandler
的代理方法.
//設置addScriptMessageHandler與JS對應方法名.而且設置<WKScriptMessageHandler>協議與協議方法 [[_webView configuration].userContentController addScriptMessageHandler:self name:@"getMessage"]; //WKScriptMessageHandler協議方法 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { //code NSLog(@"name = %@, body = %@", message.name, message.body); }
上方
-addScriptMessageHandler:name:
方法中name
填寫的方法名必須與window.webkit.messageHandlers.<方法名>.postMessage(<數據>)
中的方法名一致.
當JS端執行window.webkit.messageHandlers.<方法名>.postMessage(<數據>)
.
此協議方法就會被執行.,根據message.name
判斷一下本身須要執行哪步操做.message.body
便是傳輸的參數信息.(HTML中JS傳遞的數據)
self
的dealloc
打個斷點,會發現self
沒有釋放!這顯然是不行的!谷歌後看到一種解決方法,以下思路是:能夠另外建立一個代理對象,而後經過代理對象回調指定的self.
WeakScriptMessageDelegate
//.h @interface WeakScriptMessageDelegate : NSObject<WKScriptMessageHandler> @property (nonatomic, assign) id<WKScriptMessageHandler> scriptDelegate; - (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate; @end
//.m @implementation WeakScriptMessageDelegate - (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate { self = [super init]; if (self) { _scriptDelegate = scriptDelegate; } return self; } - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { [self.scriptDelegate userContentController:userContentController didReceiveScriptMessage:message]; } @end
使用WeakScriptMessageDelegate 設置代理
//設置addScriptMessageHandler與name.而且設置<WKScriptMessageHandler>協議與協議方法 [[_wkWebView configuration].userContentController addScriptMessageHandler:[[WeakScriptMessageDelegate alloc] initWithDelegate:self] name:@"方法名"];
//必須在 WebView的ViewController的- (void)dealloc{}
方法中進行銷燬. 這樣才能銷燬
- (void)dealloc { //... [[_webView configuration].userContentController removeScriptMessageHandlerForName:@"方法名"]; //... }
固然,安卓開發的盆友也是能夠經過這種方式從中獲取網頁的數據的.安卓注入的接口名稱在JS中也是會報錯的.所以也須要try-catch.
加載網頁或HTML代碼的方式與UIWebView
相同,代碼示例以下:
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds]; [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]]]; [self.view addSubview:webView];
用來追蹤加載過程(頁面開始加載、加載完成、加載失敗)的方法:
// 頁面開始加載時調用 - (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; // 在發送請求以前,決定是否跳轉 - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
WKUIDelegate
協議這個協議主要用於WKWebView
處理web界面的三種提示框(警告框、確認框、輸入框),下面是警告框的例子:
//在JS端調用alert函數時,會觸發此代理方法。 - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler; //JS端調用confirm函數時,會觸發此方法 - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler; //JS端調用prompt函數時,會觸發此方法 - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler;
JS
代碼用於在客戶端內部加入JS
代碼,並執行,示例以下:
// 圖片縮放的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 + '張圖');"; // 根據JS字符串初始化WKUserScript對象 WKUserScript *script = [[WKUserScript alloc] initWithSource:js injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES]; // 根據生成的WKUserScript對象,初始化WKWebViewConfiguration WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; [config.userContentController addUserScript:script]; _webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config]; [_webView loadHTMLString:@"<head></head><imgea src='http://www.nsu.edu.cn/v/2014v3/img/background/3.jpg' />"baseURL:nil]; [self.view addSubview:_webView];
目前,大多數App須要支持iOS7以上的版本,而WKWebView
只在iOS8後才能用,因此須要一個兼容性方案,既iOS7下用UIWebView
,iOS8後用WKWebView
。這個庫提供了這種兼容性方案:https://github.com/wangyangcc/IMYWebView