iOS 和H5之间互相调用总结

145 阅读3分钟

UIWebview 相关

1.stringByEvaluatingJavaScriptFromString

使用上面 stringByEvaluatingJavaScriptFromString 来实现。前提要先用webview 加载当前界面。

NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"intercept" ofType:@"html"]];
[self.webView loadRequest:[NSURLRequest requestWithURL:url]];

1.oc 调用JS

[self.webView stringByEvaluatingJavaScriptFromString:@"showResponse('点击了原生的按钮11111111111')"];

js的方法怎么写呢?

/**

     * 显示响应信息

     * @param response 响应信心

     */

    function showResponse(response) {

        $('#response').text(response);

        return response;

    }

2.js调oc

js调oc这里其实就是打开一个新的链接或者通过打开新的界面的方式。下面是通过 window.location = 'app://login?account=13011112222&password=123456'; location来实现的,window location 和frame的方式都可以的。

oc这边只要拦截是否打开新的界面就可以获取的到。

#pragma mark - UIWebViewDelegate

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {

    

    NSString *scheme = request.URL.scheme;

    NSString *host = request.URL.host;

    

    // 一般用作交互的链接都会有一个固定的协议头,这里我们一“app”作为协议头为了,实际项目中可以修改

    if ([scheme isEqualToString:@"app"]) { // scheme为“app”说明是做交互的链接

        if ([host isEqualToString:@"login"]) { // host为“login”对应的就是登录操作

            NSDictionary *paramsDict = [request.URL getURLParams];

            NSString *account = paramsDict[@"account"];

            NSString *password = paramsDict[@"password"];

            

            NSString *msg = [NSString stringWithFormat:@"执行登录操作,账号为:%@,密码为:%@", account, password];

            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"原生弹窗" message:msg delegate:nil cancelButtonTitle:@"好" otherButtonTitles:nil, nil];

            [alert show];

        } else if ([host isEqualToString:@"share"]) { // host为“share”对应的就是分享操作

            NSDictionary *paramsDict = [request.URL getURLParams];

            NSString *title = [paramsDict[@"title"] stringByRemovingPercentEncoding];

            NSString *desc = [paramsDict[@"desc"] stringByRemovingPercentEncoding];

            

            NSString *msg = [NSString stringWithFormat:@"执行分享操作,title为:【%@】,desc为:【%@】", title, desc];

            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"原生弹窗" message:msg delegate:nil cancelButtonTitle:@"好" otherButtonTitles:nil, nil];

            [alert show];

        }

        

        return NO;

    }

    return YES;

}

2.JSContext 方式

@property(nonatomic, strong) JSContext *jsContext;



// 加载测试用的HTML页面

    NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"jscontext" ofType:@"html"]];
    [self.webView loadRequest:[NSURLRequest requestWithURL:url]];

1.JS调用OC

[self.jsContext evaluateScript:@"showResponse('点击了按钮1111111111111111')"];

oc被调用的写法,其实这里和前一个的写法类似

/**

     * 显示响应信息

     * @param response 响应信心

     */

    function showResponse(response) {

        $('#response').text(response);

    }

1.OC调用JS

下面是oc的写法

app.login("13011112222", "123456");

js 像上面调用,有两个问题,app是哪个对象,app这个对象的login方法是什么呢?

下面就是核心代码

//这种方式首先都要这样写
self.jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exception) {

        context.exception = exception;

        NSLog(@"异常信息:%@", exception);

    };

    

//定义js文件中调用方法的对象是谁,这里定义了js 对象的里面的app对象就是JSContextModel
//app 调 app.login("13011112222", "123456");就是说在在调用 JSContextModel 的 login方法
self.jsContext[@"app"] = [[JSContextModel alloc] init];

下面就是在JSContextModel 对象里面定义的login 方法了。

#import <Foundation/Foundation.h>

#import <JavaScriptCore/JavaScriptCore.h>

  


@protocol JsContextExport<JSExport>

/**

 * 登出方法,js调用的方法名也是logout

 */

- (void)logout;

  


/**

 * 登录方法,JSExportAs的作用就是给OC方法导出一个js方法名,例如下面的方法js调用就是 login("your account", "your password")。在多参数的方法声明时必须使用这种方式

 */

JSExportAs(login, - (void)loginWithAccount:(NSString *)account password:(NSString *)password);

  


/**

 * 获取登录信息

 * @return 当前登录用户的身份信息。JSContext方式调用OC时,方法的返回值只能是NSString、NSArray、NSDictionary、NSNumber、BooL,其他类型不能解析

 */

- (NSDictionary *)getLoginUser;

@end

  


@interface JSContextModel : NSObject<JsContextExport>

  


@end



@implementation JSContextModel

- (void)logout {

    [[NSOperationQueue mainQueue] addOperationWithBlock:^{

        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"原生弹窗" message:@"执行【登出】操作" delegate:nil cancelButtonTitle:@"好" otherButtonTitles:nil, nil];

        [alert show];

    }];

}

  


- (void)loginWithAccount:(NSString *)account password:(NSString *)password {

    NSString *msg = [NSString stringWithFormat:@"执行登录操作,账号为:%@,密码为:%@", account, password];

    [[NSOperationQueue mainQueue] addOperationWithBlock:^{

        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"原生弹窗" message:msg delegate:nil cancelButtonTitle:@"好" otherButtonTitles:nil, nil];

        [alert show];

    }];

}

  


- (NSDictionary *)getLoginUser {

    return @{

             @"user_id": @(666),

             @"username": @"你就说6不6",

             @"sex": @"未知",

             @"isStudent": @(NO)

             };

}

  


@end



JSExportAs的作用就是给OC方法导出一个js方法名,说白了就是起别名,在多个参数必须这个做。

WebViewJavascriptBridge方式

WebViewJavascriptBridge 是一个第三方框架。

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

image.png

原文链接

WKWebView

evaluateJavaScript

这个方式其实和UIWebView的方式基本一致。

oc调用JS

// 加载测试用的HTML页面

    NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"intercept" ofType:@"html"]];

    [self.webView loadRequest:[NSURLRequest requestWithURL:url]];

调用完不需要回调的

[self.webView evaluateJavaScript:@"showResponse('点击了原生的按钮11111111111')" completionHandler:nil];

调用完需要回调的

[self.webView evaluateJavaScript:@"showResponse('点击了原生的按钮22222222222')" completionHandler:^(id _Nullable response, NSError * _Nullable error) {

        if (error) {

            NSLog(@"%@", error);

        } else {

            NSLog(@"%@", response);

        }

    }];

JS代码

/**

     * 显示响应信息

     * @param response 响应信心

     */

    function showResponse(response) {

        $('#response').text(response);

        return response;

    }

JS调用OC

和上面UIwebView的方式一致

window.location = 'app://login?account=13011112222&password=123456';

WKWebView 拦截请求

#pragma mark - WKNavigationDelegate

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {

    

    NSURLRequest *request = navigationAction.request;

    NSString *scheme = request.URL.scheme;

    NSString *host = request.URL.host;

    

    // 一般用作交互的链接都会有一个固定的协议头,这里我们一“app”作为协议头为了,实际项目中可以修改

    if ([scheme isEqualToString:@"app"]) { // scheme为“app”说明是做交互的链接

        if ([host isEqualToString:@"login"]) { // host为“login”对应的就是登录操作

            NSDictionary *paramsDict = [request.URL getURLParams];

            NSString *account = paramsDict[@"account"];

            NSString *password = paramsDict[@"password"];

            

            NSString *msg = [NSString stringWithFormat:@"执行登录操作,账号为:%@,密码为:%@", account, password];

            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"原生弹窗" message:msg delegate:nil cancelButtonTitle:@"好" otherButtonTitles:nil, nil];

            [alert show];

        } else if ([host isEqualToString:@"share"]) { // host为“share”对应的就是分享操作

            NSDictionary *paramsDict = [request.URL getURLParams];

            NSString *title = [paramsDict[@"title"] stringByRemovingPercentEncoding];

            NSString *desc = [paramsDict[@"desc"] stringByRemovingPercentEncoding];

            

            NSString *msg = [NSString stringWithFormat:@"执行分享操作,title为:【%@】,desc为:【%@】", title, desc];

            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"原生弹窗" message:msg delegate:nil cancelButtonTitle:@"好" otherButtonTitles:nil, nil];

            [alert show];

        }

        

        // ... 这里可以继续加

        

        decisionHandler(WKNavigationActionPolicyCancel);

        return;

    }

    decisionHandler(WKNavigationActionPolicyAllow);

}

webKit的方式

OC 调用JS

#define FUNC_LOGIN @"login"

#define FUNC_LOGOUT @"logout"
WKUserContentController *confVc = self.webView.configuration.userContentController;

    [confVc addScriptMessageHandler:self name:FUNC_LOGIN];

    [confVc addScriptMessageHandler:self name:FUNC_LOGOUT];

    // 加载测试用的HTML页面

    NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"webkit" ofType:@"html"]];

    [self.webView loadRequest:[NSURLRequest requestWithURL:url]];

真正的调用代码

[self.webView evaluateJavaScript:@"showResponse('点击了原生的按钮22222222222')" completionHandler:^(id _Nullable response, NSError * _Nullable error) {

        if (error) {

            NSLog(@"%@", error);

        } else {

            NSLog(@"%@", response);

        }

    }];

下面是对应的JS代码

function showResponse(response) {

       $('#response').text(response);

       return '调用成功';

    }

JS 调用OC

window.webkit.messageHandlers.login.postMessage({

                'account': '13000000000',

                'password': '123456'

            });

oc那边类似于上面的拦截

#pragma mark - WKScriptMessageHandler

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {

    if ([message.name isEqualToString:FUNC_LOGIN]) {

        if (![message.body isKindOfClass:[NSDictionary class]]) {

            return;

        }

        NSDictionary *data = message.body;

        NSString *account = data[@"account"];

        NSString *password = data[@"password"];

        

        NSString *msg = [NSString stringWithFormat:@"执行登录操作,账号为:%@,密码为:%@", account, password];

        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"原生弹窗" message:msg delegate:nil cancelButtonTitle:@"好" otherButtonTitles:nil, nil];

        [alert show];

        return;

    }

    

    if ([message.name isEqualToString:FUNC_LOGOUT]) {

        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"原生弹窗" message:@"执行登出操作" delegate:nil cancelButtonTitle:@"好" otherButtonTitles:nil, nil];

        [alert show];

        return;

    }

}

WebViewJavascriptBridge方式

看前面webview的部分