JavaScriptCore和原生页面交互
主要主要总结一下利用JavaScriptCore和UIWebView交互过程中遇到的问题。分三部分讲解
- JS调用原生
- OC调用JS
- 传值
JSContext可以将它视为一个盒子(字典),盒子中装了所有的webview加载的网页的代码(和H5的document对象好像),通过这个JSContext对象可以拿到盒子中的任意一个元素或者JS方法(通过方法名字符串),JSContext拿出来的东西 是JSValue对象。JSValue对象是对js中的对象或者方法进行的封装。
JS调用原生方法
首先导入JavaScriptCore,初始化webview,实现webview代理方法,然后再代理方法- (void)webViewDidFinishLoad:(UIWebView *)webView
中实现初始化JSContext对象代码如下:
//对JSContext对象进行初始化
self.context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
- 利用block进行的调用
self.context[@"webAudio"] = ^(NSString *speakingWords){
NSLog(@"speakingwords====%@",speakingWords);
};
其中webAudio为js中的方法名 block的参数为js调用webAudio方法的时候给webAudio方法传递的参数,相当于将js的方法直接用block替换了。所以在JS调用JS的webAudio方法的时候,实际是在调用block,OC中可以在block中实现调用OC的方法,完成JS调用OC方法。
- 利用协议JSExport
一. 将JS中JS方法的调用者设置为控制器对象。
二. 自定义一个协议继承自JSExport协议,并定义OC中将调用的方法
self.context[@"wjyg"] = self;
@protocol WebExpert <JSExport>
JSExportAs(toPage, -(void)jumpToSpeakingView:(NSString*)passWords);
@end
JS代码如下
function toPageForWJYG(data){
try {
window.wjyg && wjyg.toPage(data);
} catch(e){
alert(e.message);
}
}
JS中通过调用wjyg对象的toPage方法进行传值,第一步将controller对象赋值给js中的wjyg对象,相当于将对象进行了替换。
第二步,实现协议 将toPage方法替换为了controller中的-(void)jumpToSpeakingView:(NSString)passWords。
之后js调用wjyg.toPage(data)的时候,相当于controller调用了
controller的-(void)jumpToSpeakingView:(NSString)passWords方法,
OC调用JS方法
一 .通过利用context和JS方法名字符串拿到JSValue包装的JS方法(也就是JSValue对象)
二 . JSValue对象调用JSValue的方法就可以了(主要方法是callWithArguments:)。
JSValue *permissionValue = self.context[@"getListenPermission"];
[permissionValue callWithArguments:@[@"true"]];
OC之间传值
在以上的方法调用之间有值的传递。仔细看代码。
JavaScriptBridge和OC交互
JavaScriptBridge说白了就相当于一个中间人,注册方法和调用方法就像卖东西和买东西,注册方法相当于卖家告诉bridge自己有某个东西,调用方法就像是买家告诉bridge自己就想买某个东西
需要在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 = 'https://__bridge_loaded__';
document.documentElement.appendChild(WVJBIframe);
setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
}
在OC中调用的时候注意一下问题
- 给webivew设置bridge需要在webview创建之后就可以(另外不能重复设置也就是如下代码只调用一次就行了)
_bridge = [WebViewJavascriptBridge bridgeForWebView:_webView];
[_bridge setWebViewDelegate:self];
-
JS调用原生的时候,原生register方法的时机(也就是registerhander的方法应该放在webviewloadfinish代理方法中).因为要等到所有js文件加载完成之后才能想bridge注册方法.
-
js端调用代码的时候不能通过ID获取已存在的button变量并添加onclick事件,这样会导致客户端点击按钮的时候第一次点击没有效果(注册事件的回调block不走),可以在点击事件中直接调用
setupWebViewJavascriptBridge(function(bridge) {
bridge.callHandler('checkLoginWithUrl',{'hrefStr':"http://www.baidu.com?"}, function(response) {
})
})
setupWebViewJavascriptBridge(function(bridge) {
bridge.callHandler('checkLogin', function(response) {
})
})
网友评论