总结一些JS和OC交互的几种用法
序言
oc和js交互是现在开发中经常容易碰到的需求,那么有哪些交互方法呢?
一. 以UIWebview为例 使用JavaScriptCore来实现
- 初始化webview
self.webView = [[UIWebView alloc]initWithFrame:rect];
[self.webView loadHTMLString:htmlString baseURL:nil];
self.webView.delegate = self;
[self.view addSubview:self.webView];
-
实现代理方法
- (void)webViewDidFinishLoad:(UIWebView *)webView
-
初始化JSContext
JSContext *jsContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
获取JavaScript运行的上下文环境
下面我们来动态的写入js代码:
[jsContext evaluateScript:@"var name = ['hello','123','我是按钮']"];
这里我们往js上下文中插入了一个name数组,当然也可以添加其他js代码。
还有一种方式,直接为js定义变量或者直接赋值
NameModel *model = [[NameModel alloc]init];
model.name = @"跳转到WK";
jsContext[@"ocModel"] = model;
这样我们就可以把model对象赋值到js的ocModel变量中,在js中也可以取到name的值
同样我们知道在js里,方法也是一种对象,所以我们还可以为js里的方法用oc重写方法的实现:
jsContext[@"showMessage"] = ^(){
NSArray *args = [JSContext currentArguments];
NSLog(@"回调到了 -- %@",args);
};
如此在js中调用了showMessage方法便会回调到oc的这个block中来。 [JSContext currentArguments]指的是方法传过来的参数,因为可能有多个,所以返回的是一个数组
那么OC同样也可以直接调用js里的方法用callWithArguments
方法,具体例子:
[[JSContext currentContext][@"addItems"] callWithArguments:@[@"按钮啊"]];
上诉代码的意思是,OC直接调用JS的addItems方法,并传入一个内容为String的参数。
二. 通过WKWebView来实现交互
- 初始化WKWebView
self.webView = [[WKWebView alloc]initWithFrame:rect configuration:config];
self.webView.navigationDelegate = self;
self.webView.UIDelegate = self;
[self.webView loadHTMLString:htmlString baseURL:nil];
[self.view addSubview:self.webView];
注意:
初始化WKWebView的时候需要传入一个config, 这里也整理了config相关参数的定义及其设置:
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
// 创建设置对象
WKPreferences *preference = [[WKPreferences alloc]init];
//最小字体大小 当将javaScriptEnabled属性设置为NO时,可以看到明显的效果
preference.minimumFontSize = 0;
//设置是否支持javaScript 默认是支持的
preference.javaScriptEnabled = YES;
// 在iOS上默认为NO,表示是否允许不经过用户交互由javaScript自动打开窗口
preference.javaScriptCanOpenWindowsAutomatically = YES;
config.preferences = preference;
// 是使用h5的视频播放器在线播放, 还是使用原生播放器全屏播放
config.allowsInlineMediaPlayback = YES;
//设置视频是否需要用户手动播放 设置为NO则会允许自动播放
config.mediaTypesRequiringUserActionForPlayback = YES;
//设置是否允许画中画技术 在特定设备上有效
config.allowsPictureInPictureMediaPlayback = YES;
//设置请求的User-Agent信息中应用程序名称 iOS9后可用
config.applicationNameForUserAgent = @"ChinaDailyForiPad";
// //自定义的WKScriptMessageHandler 是为了解决内存不释放的问题
// WeakWebViewScriptMessageDelegate *weakScriptMessageDelegate = [[WeakWebViewScriptMessageDelegate alloc] initWithDelegate:self];
// //这个类主要用来做native与JavaScript的交互管理
// WKUserContentController * wkUController = [[WKUserContentController alloc] init];
// //注册一个name为jsToOcNoPrams的js方法
// [wkUController addScriptMessageHandler:weakScriptMessageDelegate name:@"jsToOcNoPrams"];
// [wkUController addScriptMessageHandler:weakScriptMessageDelegate name:@"jsToOcWithPrams"];
// config.userContentController = wkUController;
其实做完上述步骤,并不能正常显示一个h5,打开你会发现里面的内容变的很小。
这时候 你需要加这么一段代码 就可以恢复正常大小了:
//以下代码适配文本大小,由UIWebView换为WKWebView后,会发现字体小了很多,这应该是WKWebView与html的兼容问题,解决办法是修改原网页,要么我们手动注入JS
NSString *jSString = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";
//用于进行JavaScript注入
WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jSString injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
[config.userContentController addUserScript:wkUScript];
以上完成了一个WKWebView正常展示的步骤。
- 插入一段js代码:
[self.webView evaluateJavaScript:@"addItems2('测试测试')" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
NSLog(@"%@ ---- %@",result,error.localizedDescription);
}];
- WKWebView还可以截获Alert弹框,通过UIDelegate
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
NSLog(@"here showed alert");
UIAlertController *controller = [UIAlertController alertControllerWithTitle:@"提醒" message:message preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *action = [UIAlertAction actionWithTitle:@"知道了" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[self.webView evaluateJavaScript:@"messageHandle:OC" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
}];
}];
[controller addAction:action];
[self presentViewController:controller animated:YES completion:^{
completionHandler();
}];
}
注意:
这里completionHandler();
是必须要调用的 不然会crash!
三. 通过WebViewJavascriptBridge来实现交互
这种方式,是OC最省力的一种方式,反而h5需要做的事情稍微多点。
-
先导入WebViewJavascriptBridge
pod 'WebViewJavascriptBridge', '~> 6.0'
-
将webview与WebViewJavascriptBridge关联起来:
_bridge = [WebViewJavascriptBridge bridgeForWebView:self.webView];
[_bridge setWebViewDelegate:self];
3.oc调用js的方法:
oc代码:
[_bridge registerHandler:@"testObjcCallback" handler:^(id data, WVJBResponseCallback responseCallback) {
NSLog(@"testObjcCallback called: %@", data);
// 在这里可以调用responseCallback告诉js oc收到了
// responseCallback(@"Response from testObjcCallback");
}];
js代码:
callbackButton.onclick = function() {
bridge.callHandler('testObjcCallback', {'foo': 'bar'}, function(response) {
log('JS got response', response)
})
}
这里的data将会接收到内容为{'foo': 'bar'}的一个json字符串,内容可以自由传输
- js调用oc的方法:
oc代码:
[_bridge registerHandler:@"openAblum" handler:^(id data, WVJBResponseCallback responseCallback) {
...
}];
js代码:
bridge.callHandler('openAblum', {'index': '1'}, function(response) { });
传输的内容可以自定义!
以上便是我总结的几种常用的方法!!
附加一个实现oc从相册选择一个图片 并传到h5的demo
(写了两个比较蹩脚的h5,纯属展示功能需要~~~)