前言
现在web前端发展越来越快,为了追求应用的快速开发和迭代,许多产品都会选择混合开发,在手机端嵌入web页面,那么这就会导致一个问题,
原生代码怎么和js交互?那么下边我们共同学习一下iOS和web是怎么交互的。
概述
-
UIWebView
和JavaScriptCore
的结合使用。但是我们要知道JavaScriptCore
这个框架是在iOS7
之后官方才发布。 -
iOS8
之后的WKWebView
的使用 - 请求路径拦截的使用(
WebViewJavascriptBridge
第三方库)
我们还需要先了解一下JavaScriptCore框架的基本使用
基本使用
1、UIWebView和js的交互
OC调用js
使用UIWebview
的stringByEvaluatingJavaScriptFromString
方法执行js
代码,或注入js
代码
js调用OC
1、使用UIWebview
想要js
调用OC
代码我们只能通过拦截的方式,代码如下:
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSString *url = request.URL.absoluteString;
if ([url rangeOfString:@"myProtocol://"
].location != NSNotFound) {
NSLog(@"拦截成功了");
return NO;
}
return YES;
}
2、 在iOS7之后我们可以使用JavaScriptCore
进行和js
的通信
使用方式
先创建一个JSProtocol协议,遵循JSExport协议,添加一个打印方法
@protocol JSProtocol
-(void)print:(NSString *)str;
@end
创建一个MyJSObject对象,遵循JSProtocol,实现print方法
-(void)print:(NSString *)str
{
NSLog(@"log=%@",str);
}
然后当web页面加载完毕的时候,通过[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]
获取JSContext
对象
OC代码如下:
-(void)webViewDidFinishLoad:(UIWebView *)webView
{
self.title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
NSLog(@"context=%@",context);
self.context = context;
// 为js增加log方法
context[@"log"] = ^(NSString *str){
NSLog(@"log:%@",str);
};
// 为js增加obj对象 遵循该协议jsExport
MyJSObject *obj = [[MyJSObject alloc] init];
context[@"obj"] = obj;
// 为js增加changeLabel的函数
context[@"changeLabel"] = ^(NSString *str){
self.text.text = str;
};
context.exceptionHandler = ^(JSContext *context, JSValue *exception) {
NSLog(@"异常%@",exception);
};
}
html代码
这里通过JSContext
为js
增加了log
和changeLabel
全局方法,挂载到window
对象下边。当js
调用log
或者changeLabel
方法时会执行block
内的代码,当传入相应的参数,就可以达到js
想OC
传递参数和交互。
效果图
2、WKWebView和js的交互
WKWebView
是iOS8
之后才有,所以如果你的app
最低支持iOS
可以尽情使用。
OC调用js
使用WKWebView
的evaluateJavaScript
方法执行js
代码,或注入js
代码
js调用OC
要想WKWebView
和js
交互,需要用到WKUserContentController
,下面我们来看一下如何初始化WKWebView
// 创建配置对象
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
// 为WKWebViewConfiguration设置偏好设置
WKPreferences *preferences = [[WKPreferences alloc] init];
configuration.preferences = preferences;
// 允许native和js交互
preferences.javaScriptEnabled = YES;
WKUserContentController *userContentController = [[WKUserContentController alloc] init];
[userContentController addScriptMessageHandler:self name:@"event"];
configuration.userContentController = userContentController;
self.userContentController = userContentController;
// 初始化webview
WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 64, self.view.frame.size.width, 300) configuration:configuration];
// 此处替换你本机的ip
NSURL *url = [NSURL URLWithString:@"http://172.26.233.156:3001/wkWebView"];
webView.navigationDelegate = self;
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
[webView loadRequest:request];
[self.view addSubview:webView];
self.webView = webView;
首先我们先创建WKWebViewConfiguration
配置对象,然后创建WKUserContentController
对象,然后我们使用WKUserContentController
的addScriptMessageHandler
注册要接收消息的名称,这样我们才可以在js代码中通过window.webkit.messageHandlers.名称.postMessage
发送消息,那么OC怎么接收消息呢?
下边是接收消息的代理
// 接受js发送的消息
-(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
NSLog(@"%@,%@",message.name,message.body);
NSString *name = message.name;
if ([name isEqualToString:@"event"]) {
// 打开相册
if ([message.body isEqualToString:@"打开相册"]) {
UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init];
imagePickerController.delegate = self;
[self.navigationController presentViewController:imagePickerController animated:YES completion:nil];
}else {
}
}
}
一定要记得在dealloc方法移除监听名称
[self.userContentController removeScriptMessageHandlerForName:@"event"];
html代码
效果图
源码
源码
这里边有两个工程,一个是iOS工程,一个是node工程,用来创建一个web服务器模拟加载远程的web项目
WebViewInteractiveIOS 目录是iOS
WebViewInteractiveWeb web项目
运行
iOS运行这里不多赘述,需要注意的是把webview加载的ip换成本机ip
web运行,执行如下命令,前提是装了node环境的机器
cd WebViewInteractiveWeb
npm install
npm run server
3、 JSBridge的使用
源码
iOS端webview和jsbridge的使用实现
首先创建一个iOS
工程,引入WebViewJavascriptBridge
第三方框架,使用pod
还是自己手动导入都可以
下面看一下具体实现
创建WKWebview
,初始化WebViewJavascriptBridge
,设置WebViewJavascriptBridge
,WKWebView
的代理
// 初始化webview
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
// 此处替换你本机的ip
NSURL *url = [NSURL URLWithString:kHOSTURL];
webView.navigationDelegate = self;
webView.UIDelegate = self;
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
[webView loadRequest:request];
[self.view addSubview:webView];
// self.webView = webView;
self.view.backgroundColor = [UIColor whiteColor];
// 开启日志
[WebViewJavascriptBridge enableLogging];
self.bridge = [WebViewJavascriptBridge bridgeForWebView:webView];
[self.bridge setWebViewDelegate:self];
前端js手动注册和获取JSBridge
/*这段代码是固定的,必须要放到js中*/
function setupWebViewJavascriptBridge(callback) {
if (window.WebViewJavascriptBridge) {
return callback(WebViewJavascriptBridge);
}
if (window.WVJBCallbacks) {
return window.WVJBCallbacks.push(callback);
}
window.WVJBCallbacks = [callback];
var WVJBIframe = document.createElement('iframe');
WVJBIframe.style.display = 'none';
WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__';
document.documentElement.appendChild(WVJBIframe);
setTimeout(function() {
document.documentElement.removeChild(WVJBIframe)
}, 0)
}
// 初始化jsbridge
setupWebViewJavascriptBridge(function(bridge) {
global.bridge = bridge;// 获取到jsbridge;
})
OC
调用js
首先需要js注册交互
this.$bridge.registerhandler('ocCallJs', (data, responseCallback) => {
alert(data)
responseCallback(data)
});
iOS
端需要WebViewJavascriptBridge
使用callHandler
调用js
注册的方法
[self.bridge callHandler:@"ocCallJs" data:@"123" responseCallback:^(id responseData) {
NSLog(@"responseData=%@",responseData);
}];
js
调用OC
需要iOS
注册一个交互以供js
回调
[self.bridge registerHandler:@"jsCallOc" handler:^(id data, WVJBResponseCallback responseCallback) {
//将base64字符串转为NSData
NSData *decodeData = [[NSData alloc]initWithBase64EncodedString:data options:NSDataBase64DecodingIgnoreUnknownCharacters];
//将NSData转为UIImage
UIImage *decodedImage = [UIImage imageWithData: decodeData];
TwoController *twoCtr = [[TwoController alloc] init];
twoCtr.view.backgroundColor = [UIColor purpleColor];
[self.navigationController pushViewController:twoCtr animated:YES];
twoCtr.image = decodedImage;
if (responseCallback) {
// 反馈给JS
responseCallback(@{@"jsCallOc的参数1": @"jsCallOc的参数2"});
}
}];
js调用交互
this.$bridge.callhandler("jsCallOc",image.replace("data:image/jpeg;base64,",""),(responseData)=>{
console.log("responseData",responseData);
})
效果图
Hybrid之JSBridge的实现原理(WebViewJavascriptBridge源码分析)这一篇文章讲述了JSBridge的交互流程和WebViewJavascriptBridge
的实现原理。有兴趣的小伙伴可以看这一篇文章,谢谢
其它推荐文章
React
react之组件的生命周期
react+webpack入门指南
react项目整合express+mock实现模拟接口数据
react+webpack4搭建前端项目(一)
RN项目
菜谱项目 源码
天气预报 源码
RN插件
键盘自适应插件 源码
小程序开发
小程序开发知识总结