native与web交互的那些事儿

 

项目中经常会出现需要native和web交互的地方,我推荐两种方法。一种是通过JavaScriptCore,一种是通过拦截网络请求的方式

第一种方式

1、先介绍几个名词:

  • JSContext:给JavaScript提供运行的上下文环境
  • JSValue:JavaScript和Objective-C数据和方法的桥梁
  • JSExport:这是一个协议,如果采用协议的方法交互,自己定义的协议必须遵守此协议

2、先来看个html

<html>
    <head>
        <meta charset="UTF-8">
        <script>
            var callShare = function()
            {
                var shareInfo = JSON.stringify({"title": "标题", "desc": "内容", "shareUrl": "http://www.baidu.com","shareIco":"https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/logo_white_fe6da1ec.png"});
                HH.shareToObjc(shareInfo);
            }
            var picCallback = function(photos)
            {
                alert(photos);
            }
            
            var shareCallback = function()
            {
                alert('success');
            }
        </script>
</head>
<body>
    <h1>Objective-C和JavaScript交互的那些事</h1>
        <div>
            <input type="button" value="调用相册" onclick="HH.callCamera()">
        </div>
        <div>
            <input type="button" value="分享字符串" onclick="callShare()">
        </div>
</body>
</html>
View Code

  从Html中的JavaScript函数 HH.shareToObjc(shareInfo) 函数指向了native方法

3、看个native文件

#import "ViewController.h"
#import <JavaScriptCore/JavaScriptCore.h>

@protocol JSObjcDelegate <JSExport>
/**
 *  调用相册
 */
- (void)callCamera;
/**
 *  分享字符串
 *
 *  @param shareString 字符串
 */
- (void)shareToObjc:(NSString *)shareString;

@end

@interface ViewController ()<UIWebViewDelegate,JSObjcDelegate>

@property (nonatomic,strong)  JSContext *jsContext;
@property (weak, nonatomic) IBOutlet UIWebView *webView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSURL* url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"test" ofType:@"html"]];
    [self.webView loadRequest:[NSURLRequest requestWithURL:url]];
}

#pragma mark UIWebViewDelegate

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    self.jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    self.jsContext[@"HH"] = self;
    self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exception)
    {
        NSLog(@"异常信息:%@",exception);
    };
}

#pragma mark JSObjcDelegate

- (void)shareToObjc:(NSString*)shareString
{
    NSLog(@"%@",shareString);
    [self.jsContext[@"shareCallback"]callWithArguments:nil];
}

- (void)callCamera
{
    JSValue* picCallback = self.jsContext[@"picCallback"];
    [picCallback callWithArguments:@[@"图片"]];
}
@end
View Code

  自定义JSObjcDelegate协议,而且此协议必须遵守JSExport这个协议,自定义协议中的方法就是暴露给web页面的方法。在webView加载完毕的  时候获取JavaScript运行的上下文环境,然后再注入桥梁对象名为HH,承载的对象为self即为此控制器,控制器遵守此自定义协议实现协议中对应的方法。在JavaStript调用完本地应用的方法做完相对应的事情之后,又回调了JavaStript中对应的方法,从而实现了web页面和本地应用之间的通讯。

第二种方式

1、在JavaScript代码中使用 window.location.href = "HH://";  //自定义协议

2、在Native中使用以下代码来拦截网络请求

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    NSString *url = request.URL.absoluteString;
    if ([url rangeOfString:@"HH://"].location != NSNotFound) { 
        NSLog(@"callCamera");
        return NO;
    }
    return YES;
}
View Code

拦截到网络请求之后对不通的自定义协议进行分发处理,但是这样就无法通过回调的方式调用JavaScript中的回调函数了,可以通过调用stringByEvaluatingJavaScriptFromString函数将数据传递给JavaScript达到相同的效果

 

你可能感兴趣的:(native与web交互的那些事儿)