iOS JS和原生的交互方法

1、採用攔截URL請求的方式

1. 事先和前端定好要攔截的URL,實現UIWebView的代理方法shouldStartLoadWithRequest,在方法中對實現定義好的URL進行攔截,攔截到後處理原生邏輯,及回調。

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    if ([request.URL.absoluteString hasSuffix:@"js_native://alert"]) {
        //這個是彈框
        [self alertWidthValue:request.URL.absoluteString];
        //這個是回調
        [webView stringByEvaluatingJavaScriptFromString:@"jsCallBackNativeMethod('我是回調')"];
    }
    return YES;
}


H5代碼
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
            <title>js和原生的交互</title>
    </head>
    <body>
        
        <h1>js和原生的交互 調用原生彈框</h1>
        <button onclick="jsCallNativeMethod()" style="border: 1px solid black">經過攔截URL彈原生彈框</button>
        
        </body>
</html>
<script>
    function jsCallNativeMethod() {
        //能夠傳參數拼在後面就行
        location.href = "js_native://alert";
    }
    //原生回調js方法
     function jsCallBackNativeMethod(arguments) {
        alert('原生調用js方法 傳來的參數 = ' + arguments);
    }
  </script>

原生回調js
[self.webView stringByEvaluatingJavaScriptFromString:@"jsCallBackNativeMethod('我是回調')"];
複製代碼

2、經過JavaScriptCore

步驟:
1. 和前端約定好要傳的參數和方法名、回調方法等。
2. 實現webViewDidFinishLoad代理方法,獲取js,在裏寫實現要調用的原生方法,接收傳來的參數。
3. 利用evaluateScript執行回調html的方法
複製代碼

實現方法一:直接獲取js中定義好的方法html

- (void)webViewDidFinishLoad:(UIWebView *)webView{
    JSContext *jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    __weak typeof(self) weakSelf = self;
    //nativeAlert js定義的方法
    jsContext[@"nativeAlert"] = ^(){
    //傳來的參數
        NSArray *arguments = [JSContext currentArguments];
        NSLog(@"%@",arguments);
        NSString *value = [NSString stringWithFormat:@"%@",[arguments objectAtIndex:0]];
        [self alertWidthValue:value];
        [weakSelf handleOtherOperating];
    };
}

- (void)handleOtherOperating {
    // 其餘處理
    NSLog(@"原生處理方法 thread = %@", [NSThread currentThread]);
    // 原生回調js方法
    JSContext * context = [_webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    [context evaluateScript:@"nativeCallbackJscMothod('我是原生傳過去的參數')"];
    
}

h5代碼
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
            <title>js和原生的交互</title>
    </head>
    <body>
        
        <h1>js和原生的交互 調用原生彈框</h1>
    
        
        <button onclick="jscCallNativeMethod()" style="border: 1px solid black">經過JavaScriptCore實現JS原生交互</button>
        


    </body>
</html>
<script>


    function jscCallNativeMethod() {
        nativeAlert('我是傳來的參數1', '我是傳來的參數2');
    }
    // 原生的回調方法 能夠接收原生傳來的參數
    function nativeCallbackJscMothod(arguments) {
        alert('原生調用js方法 傳來的參數 = ' + arguments);
    }
</script>

複製代碼

實現方法二:利用JSExport協議來處理前端

步驟:
1. 在html頁面中定義須要調用原生的方法、原生回調JS的方法
2. 建立一個工具類實現一個遵照JSExport的協議,提供js須要調用的方法
3. 在webViewDidFinishLoad中利用JSContext將這個類暴露給html
複製代碼
//建立一個工具類實現一個遵照JSExport的協議,提供js須要調用的方法
#import <Foundation/Foundation.h>
#import <JavaScriptCore/JavaScriptCore.h>


NS_ASSUME_NONNULL_BEGIN
///建立一個遵循JSExport的協議
@protocol JSToolProtocol <NSObject,JSExport>

// 提供給js調用原生的方法,若是想暴露一些屬性也是能夠的。
- (void)jsCallNativeMethod;


@end

@interface JSTool : NSObject<JSToolProtocol>

@property (nonatomic, strong) JSContext * jsContext;


@end

NS_ASSUME_NONNULL_END



@implementation JSTool

- (void)jsCallNativeMethod {
    NSLog(@"js 調用 原生方法");
    // 若是還想要調用js的方法就須要拿到webView的JSContext
    [self.jsContext evaluateScript:@"nativeCallbackJscMothod('原生傳遞的參數')"];
}


@end



<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
            <title>js和原生的交互</title>
    </head>
    <body>
        
        <h1>js和原生的交互 調用原生彈框</h1>
        <button onclick="jseCallNativeMethod()" style="border: 1px solid black">經過JSExport協議實現JS原生交互</button>

    </body>
</html>
<script>
        function jseCallNativeMethod() {
        jsTool.jsCallNativeMethod();//要和工具類定義的方法同樣
    }
    // 原生的回調方法 能夠接收原生傳來的參數
    function nativeCallbackJscMothod(arguments) {
        alert('原生調用js方法 傳來的參數 = ' + arguments);
    }
</script>


- (void)webViewDidFinishLoad:(UIWebView *)webView{
    JSContext *jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];    
    JSTool *jsTool = [[JSTool alloc]init];
    jsTool.jsContext = jsContext;
//    方式一
//    jsContext[@"jsTool"] = jsTool;
//    方式二
    [jsContext setObject:jsTool forKeyedSubscript:@"jsTool"];
}

複製代碼

3、經過WKWebView

* WKWebViewConfiguration用來初始化WKWebView的配置。
* WKPreferences配置webView可否使用JS或者其餘插件等
* WKUserContentController用來配置JS交互的代碼
* UIDelegate用來控制WKWebView中一些彈窗的顯示(alert、confirm、prompt)。
* WKNavigationDelegate用來監聽網頁的加載狀況,包括是否容許加載,加載失敗、成功加載等一些列代理方法。
複製代碼
// 攔截URL
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    
    NSURL * url = navigationAction.request.URL;
    NSString * scheme = url.scheme;
    NSString * query = url.query;
    NSString * host = url.host;
    if ([[url absoluteString] hasSuffix:@"js_native://alert"]) {
        [self handleJSMessage];
        decisionHandler(WKNavigationActionPolicyCancel);
        return;
    }
    decisionHandler(WKNavigationActionPolicyAllow);
}

- (void)handleJSMessage {
    // 回調JS方法
    [_wkWebView evaluateJavaScript:@"nativeCallbackJscMothod('123')" completionHandler:^(id _Nullable x, NSError * _Nullable error) {
        NSLog(@"x = %@, error = %@", x, error.localizedDescription);
    }];
}
#pragma mark - WKUIDelegate
// 處理JS中回調方法的alert方法 JS端調用alert()方法會觸發下面這個方法,而且經過message獲取到alert的信息
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
    UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"舒適提示" message:message preferredStyle:UIAlertControllerStyleAlert];
    [alert addAction:[UIAlertAction actionWithTitle:@"肯定" style:UIAlertActionStyleCancel handler:nil]];
    [self presentViewController:alert animated:YES completion:nil];
    completionHandler();
}

- (WKWebView *)wkWebView{
    if (!_wkWebView) {
        //初始化WKWebView的配置
        WKWebViewConfiguration * configuration = [[WKWebViewConfiguration alloc] init];
//        配置JS交互的代碼
        configuration.userContentController = [WKUserContentController new];
//        配置webView可否使用JS或者其餘插件等
        WKPreferences * preferences = [WKPreferences new];
        preferences.javaScriptCanOpenWindowsAutomatically = YES;
        preferences.minimumFontSize = 50.0;
        configuration.preferences = preferences;
        
        _wkWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(_webView.frame), [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height/2) configuration:configuration];
        _wkWebView.navigationDelegate = self;
        _wkWebView.UIDelegate = self;
        [self.view addSubview:_wkWebView];
        
    }
    return _wkWebView;
}


<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
            <title>js和原生的交互</title>
    </head>
    <body>
        
        <h1>js和原生的交互 調用原生彈框</h1>
        <button onclick="jsCallNativeMethod()" style="border: 1px solid black">經過攔截URL彈原生彈框</button>
    </body>
</html>
<script>
    function jsCallNativeMethod() {
        //能夠傳參數拼在後面就行
        location.href = "js_native://alert";
    }
    // 原生的回調方法 能夠接收原生傳來的參數
    function nativeCallbackJscMothod(arguments) {
        alert('原生調用js方法 傳來的參數 = ' + arguments);
    }
</script>



複製代碼

4、經過WKScriptMessageHandler

WKWebView不支持JavaScriptCore。此時咱們可使用WKWebView的WKScriptMessageHandler。

1. 在原生代碼中利用userContentController添加JS端須要調用的原生方法
2. 實現WKScriptMessageHandler協議中惟一一個方法`didReceiveScriptMessage`
3. 在該方法中根據message.name獲取調用的方法名作相應的處理,經過message.body獲取JS端傳遞的參數
4.在JS端經過`window.webkit.messageHandlers.methodName(事先定好的名稱).postMessage(['name','參數','age', 18])`給WK發送消息`didReceiveScriptMessage`這個方法會接收到,能夠經過`message.body`獲取傳來的值。
複製代碼

1. 經過initWithFrame:configuration初始化方法,給configuration傳入WKWebViewConfiguration對象,在WKWebViewConfiguration配置和JS交互的方法。

- (WKWebView *)wkWebView{
    if (!_wkWebView) {
        //初始化WKWebView的配置
        WKWebViewConfiguration * configuration = [[WKWebViewConfiguration alloc] init];
//        配置JS交互的代碼
        configuration.userContentController = [WKUserContentController new];
//      MessageHandler添加對象 記得實現協議<WKScriptMessageHandler>  name JS發送postMessage的對象
        [configuration.userContentController addScriptMessageHandler:self name:@"jsCallNativeMethod"];
        _wkWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(_webView.frame), [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height/2) configuration:configuration];
        _wkWebView.navigationDelegate = self;
        _wkWebView.UIDelegate = self;
        [self.view addSubview:_wkWebView];
        
    }
    return _wkWebView;
}
複製代碼

2. 實現協議,而且實現didReceiveScriptMessage代理方法,當js調用jsCallNativeMethod方式時,會回調這個代理方法。

#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    if ([message.name isEqualToString:@"jsCallNativeMethod"]) {
    //這個是傳過來的參數
        NSLog(@"%@",message.body);
 // 回調JS方法
    [_wkWebView evaluateJavaScript:@"nativeCallbackJscMothod('123')" completionHandler:^(id _Nullable x, NSError * _Nullable error) {
//        NSLog(@"x = %@, error = %@", x, error.localizedDescription);
    }];
        }
}
複製代碼

3. js調用

// window.webkit.messageHandlers.<name>.postMessage(<messageBody>)
// 這個name就是設置MessageHandler的第二個參數
function jsCallNativeMethod() {
        window.webkit.messageHandlers.jsCallNativeMethod.postMessage('個人參數');
    }
複製代碼

4. 移除

- (void)dealloc {
   // 爲了不循環引用,致使控制器沒法被釋放,須要移除
    [self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"jsCallNativeMethod"];
}
複製代碼

5、經過第三方庫WebViewJavascriptBridge

WebViewJavascriptBridge 地址java

當前項目代碼地址git

相關文章
相關標籤/搜索