本人有若干成套学习视频, 可试看! 可试看! 可试看, 重要的事情说三遍 包含Java
, 数据结构与算法
, iOS
, 安卓
, python
, flutter
等等, 如有需要, 联系微信tsaievan
.
<一>WebViewJavascriptBridge
WebViewJavascriptBridge
是一套在UIWebView
和WKWebView
上都可以兼容的与JS
交互的框架
OC
与JS
交互无非就是两种情况:
-
OC
调用JS
方法 -
JS
调用OC
方法
在这套框架中, 作者巧妙地设计了bridge
这个对象, 相当于将OC
和JS
两端用桥连接了起来, bridge
这个对象是两端共有的, 不管是在OC
的代码中, 还是JS
代码中, bridge
指针都指向同一个对象
如下图所示:
![](https://img.haomeiwen.com/i2868984/addcaff67de49ae0.png)
那么当我OC
端去调JS
端方法时, 这个方法首先要在JS
端注册.
如下图所示: Call handler
是一个原生按钮, 再点击这个按钮的时候, 我需要调用JS
的方法, 并上传参数. 那么这个JS
方法首先就应该在JS
端注册
![](https://img.haomeiwen.com/i2868984/2bf82ef370d4b568.png)
JS
端代码:
bridge.registerHandler('testJavascriptHandler', function(data, responseCallback) {
log('ObjC called testJavascriptHandler with', data)
var responseData = { 'res':'Right back atcha!' }
log('JS responding with', responseData)
responseCallback(responseData)
})
JS
端注册了方法之后, 就相当于(请注意, 是相当于 !!!)给bridge
添加了一个block
, bridge
现在持有这个block
. 但并没有调用, 那什么时候调用呢. 就是OC
端去调用的时候:
OC
端代码:
- (void)callHandler:(id)sender {
id data = @{ @"greetingFromObjC": @"Hi there, JS!" };
[_bridge callHandler:@"testJavascriptHandler" data:data responseCallback:^(id response) {
NSLog(@"testJavascriptHandler responded: %@", response);
}];
}
这个方法就相当于调用了JS
端的testJavascriptHandler
方法, 并上传了@{ @"greetingFromObjC": @"Hi there, JS!" }
这样一个字典参数.
如果是JS
端去调用OC
的方法呢?
![](https://img.haomeiwen.com/i2868984/19abf7e764e4fc3c.png)
同理, OC
端就要去先注册这个方法:
[_bridge registerHandler:@"testObjcCallback" handler:^(id data, WVJBResponseCallback responseCallback) {
NSLog(@"testObjcCallback called: %@", data);
responseCallback(@"Response from testObjcCallback");
}];
这个注册方法和之前JS
端的那个注册是一模一样的
然后JS
端在适当的时候调用方法
callbackButton.onclick = function(e) {
e.preventDefault()
log('JS calling handler "testObjcCallback"')
bridge.callHandler('testObjcCallback', {'foo': 'bar'}, function(response){})
}
就是这么简单, 轻松, 还有easy
<二>WebKit
原生框架
WebKit
新增了一个WKUserContentController
这个类, 这个类其实就类似于WebViewJavascriptBridge
中的bridge
, 只不过, 它实用的是代理
的方式:
JS
端调用OC
方法:
WKUserContentController *ucc = self.webView.configuration.userContentController;
[ucc addScriptMessageHandler:self name:kShare];
[ucc addScriptMessageHandler:self name:kClose];
[ucc addScriptMessageHandler:self name:kGoMarket];
[ucc addScriptMessageHandler:self name:kNeedLogin];
[ucc addScriptMessageHandler:self name:kShareImage];
[ucc addScriptMessageHandler:self name:kTryOutVIP];
[ucc addScriptMessageHandler:self name:kShareWebView];
以上是我们公司的部分代码, addScriptMessageHandler :
方法是注册代理, 监听JS
那边的事件处理, 当JS
需要调用OC
代码时, 就会走回调方法:
- (void)userContentController:(WKUserContentController *)userContentController
didReceiveScriptMessage:(WKScriptMessage *)message
{
if ([message.name isEqualToString:kShare]) {
//
}
else if ([message.name isEqualToString:kShareImage]) {
//
}
else if ([message.name isEqualToString:kGoMarket]) {
//
}
else if ([message.name isEqualToString:kNeedLogin]) {
//
}
else if ([message.name isEqualToString:kShareWebView]) {
//
}
回调方法中message.name
就是JS
端需要调用的OC
方法, message.body
就是JS
端回传给OC
端的json
参数.
OC
端调用JS
端代码:
NSString *scriptStr = [NSString stringWithFormat:@"DoAppAction('%@',{'status':false});", @"shared"];
[self.webView evaluateJavaScript:scriptStr completionHandler:nil];
以上代码是分享成功后需要调用的JS
方法, 主要还是使用evaluateJavaScript: completionHandler:
方法来完成OC
端对JS
端的交互
<三>JavaScriptCore
JavaScriptCore
这个框架是用在UIWebView
上的, 在WKWebView
上却不适用.
同理, 在JavaScriptCore
这个框架里, 同样有一个充当桥梁的东西, 那就是JSContext
.
在网页加载完毕后, UIWebViewDelegate
会回调- (void)webViewDidFinishLoad:(UIWebView *)webView
方法, 在这个方法中, 我们可以获取到一个上下文
对象:
JSContext *jsContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
self.jsContext = jsContext;
所有的, 不管是OC
端调JS
端代码, 还是JS
调OC
端代码, 都是由JSContext
去完成的.
-
JS
调用OC
代码:
比如要让JS
端去打开手机相册.
self.jsContext[@"getImage"] = ^() {
weakSelf.imagePicker = [[UIImagePickerController alloc] init];
weakSelf.imagePicker.delegate = weakSelf;
weakSelf.imagePicker.allowsEditing = YES;
weakSelf.imagePicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
[weakSelf presentViewController:weakSelf.imagePicker animated:YES completion:nil];
};
类似于WebViewJavascriptBridge
框架的注册方法, OC
端先注册这个方法, 把这个block
通过KVC
的方式保存到上下文中. JS
端就通过方法名, 从上下文中取出这个block
执行
除了可以调用方法, 还可以通过JS
去操作OC
对象.
这里的OC
对象是遵守JSExport
协议的
JSExport
有这样一个宏: JSExportAs
, 通过这个宏我们可以简化对象方法, 使之更适合JS
调用
@protocol PersonProtocol <JSExport>
JSExportAs(show, -(void)showA:(NSString *)a andB:(NSString *)b);
@end
@interface Person : NSObject <PersonProtocol>
@end
在OC
中, 我只要将person
对象进行注册, 那么在JS
中, 我就可以随意使用这个对象, 调用这个对象的方法了:
OC
端注册:
Person *p = [Person new];
self.jsContext[@"person"] = p;
JS
端调用:
person.show('你好', '我是Martin');
这里实际上调用的就是OC
端的代码:
#import "Person.h"
@implementation Person
-(void)showA:(NSString *)a andB:(NSString *)b {
NSLog(@"%s====%@", __func__, [NSString stringWithFormat:@"%@,%@", a, b]);
}
@end
除此之外, 这个框架还为我们提供了异常处理的方法:
self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exception) {
context.exception = exception;
NSLog(@"exception == %@",exception);
NSLog(@"%@",context);
};
-
OC
调用JS
代码
有两种方法:
- 第一种方法是用上下文对象去调用
evaluateScript:
方法, 这个在WebKit
框架中是用webView
对象去调的
2.第二种方法是
[[JSContext currentContext][@"ocCalljs"] callWithArguments:@[@"arg"]];
在用当前上下文去找这个JS
方法并调用, callWithArguments:
还可以给JS
方法传参数.
以上就是我对OC
和JS
交互的几个框架简单总结, 才疏学浅, 欢迎指正.
网友评论