iOS(UIWebView 和WKWebView)OC與JS交互 之二

在iOS應用的開發過程當中,咱們常常會使用到WebView,當咱們對WebView進行操做的時候,有時會須要進行源生的操做.那麼我記下來就與你們分享一下OC與JS交互.html

首先先說第一種方法,並無牽扯OC與JS交互,只是作攔截和跳轉.前端

攔截跳轉的URL,跳轉源生界面(用起來感受怪怪的,萬一URL更換了怎麼辦.)
UIWebView
//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
//使用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);
}

 

----------✨↓↓↓↓↓↓✨----------

OC與JS交互(WebView監聽事件)

正入主題.java

一.OC調用JS

1.UIWebView

①直接運行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];
}

 

2.WKWebView
[webView evaluateJavaScript:@"執行的JS代碼" completionHandler:^(id _Nullable response, NSError * _Nullable error) {
  
}];

 

二.JS調用OC ✨✨✨✨✨✨

當網頁觸發某種操做,能夠給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()方法.服務器

UIWebView

在網頁加載完成的時候檢測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名稱需保持一致.

WKWebView

實現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傳遞的數據)

WKWebView 內存泄露
若是這時候你在selfdealloc打個斷點,會發現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.

一下是補充WKWebView用法

1、加載網頁

加載網頁或HTML代碼的方式與UIWebView相同,代碼示例以下:

 WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]]];
[self.view addSubview:webView];

 

2、加載的狀態回調 (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;
// 在發送請求以前,決定是否跳轉
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;

 

3、新的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;

 

 

4、動態加載並運行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

相關文章
相關標籤/搜索