WKWebView OC与JS交互
整体上看原理还是挺简单的,即OC端向JS中注入JS脚本或者注入一个事件对象,生成一个时间handeler,当JS触发此事件时,handler处理事件的回调用,实现相互传值和事件交互的效果。
1. OC调用JS
OC调用JS方法主要是通过执行javaScript脚本来实现的。
WKWebKit提供的主要方法是:- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler
- 例1:通过javaScript执行js里的某个函数
NSString *script = [NSString stringWithFormat:@"function('%@', '%@');", xx, xx]; [self.webView evaluateJavaScript:script completionHandler:^(id result, NSError *error) {
// ... }];
通过的webView提供的API,执行一段javaScript脚本代码,即OC调用了js中的function()这个函数,xx,xx分别是调用js函数时候传递的两个参数。
- 例2:通过evaluateJavaScript获取js某属性值
设置userAgent
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
// Set the user-Agent. [webView evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
NSString *versionIdentifier = [NSString stringWithFormat:@"%@ %@",WMS_CUSTOMER_USER_AGENT,[EIInfoPlist appVersion]]; if (![result containsString:versionIdentifier]) {
NSString *deviceIdentifier = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) ? @"iPad" :@"iPhone"; webView.customUserAgent = [NSString stringWithFormat:@"%@^%@^%@",result,deviceIdentifier,versionIdentifier]; } NSLog(@"user-Agent : %@",webView.customUserAgent); }]; }
如上,我们通过执行js的方法navigator.userAgent来获取userAgent,实际上是调用的navigator的getUserAgent()。
2. JS调用OC
JS调用OC方法,感觉更像是一种通知。
OC端需要先注册事件,这个事件可能是js端某个按钮的点击事件或者某个函数事件。
需要注入到JS中的消息handeler名称,在JS中应该会注册一个事件对象。
static NSString *const kLoadSuccessfully = @"kLoadSuccessfully";
通过以下方式注入js或者添加一个消息处理对象
// 注册事件 [self addScriptHandhelderTo:webView message:kSessionTimeOut javascript:nil]; // 这种方式是注入一段js脚本 //[self addScriptHandhelderTo:webView message:kSessionTimeOut javascript:[NSString stringWithFormat:@"function SessionTimeOut() { \n window.webkit.messageHandlers.%@.postMessage(body); \n}", kSessionTimeOut]];
- (void)addScriptHandhelderTo:(WKWebView *)webView message:(NSString *)message javascript:(NSString *)javascript {
if (message) {
WMSJavascriptHandlerDelegateBridge *delegateBridge = [[WMSJavascriptHandlerDelegateBridge alloc] initWithDelegate:self]; [webView.configuration.userContentController addScriptMessageHandler:delegateBridge name:message]; [delegateBridge release]; } if (javascript) {
WKUserScript *userScript = [[WKUserScript alloc] initWithSource:javascript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES]; [webView.configuration.userContentController addUserScript:userScript]; [userScript release]; } }
当JS触发该事件时,会通过window.webkit.messageHandlers.xx来获取到OC注入的事件对象xx,然后post一个message给OC端。
例如:
if (window.webkit && window.webkit.messageHandlers.NavigationBar) { window.webkit.messageHandlers.NavigationBar.postMessage({ ... }); }
NavigationBar就是OC端注入到JS中的一个事件对象。JS端通过 window.webkit.messageHandlers.NavigationBar这种方式,从messageHandlers里面获取到NavigationBar这个事件对象。然后通过 postMessage({ ... })发送一条消息到OC端,其中 {…} 内,可以自定义一些字段。这些字段对应的是OC端的message的body。NavigationBar对应的OC中的message的name。
再例如:
if (window.webkit && window.webkit.messageHandlers.LoadSuccessfully) {
window.webkit.messageHandlers.LoadSuccessfully.postMessage({
Result: Sucess }); }
OC端需要实现WKScriptMessageHandler回调来接收JS发送的消息
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
if ([message.name isEqualToString:kLoadSuccessfully]) {
// .. } }
message.name即JS端的事件对象,也就是OC端注册的事件对象,或者说是OC端注入到JS中的一个事件对象。
message.body即JS端发送的消息的消息体。
3. 总结
总之,JS和OC的通信,是通过OC向JS中注入事件对象或者注入JS脚本来实现的。
关键点:
- OC端必须注册事件/注入JS
- OC端需实现WKScriptMessageHandler来接收JS事件回调消息
注意:
- 添加了事件,需要在合适的地方移除事件,否则可能导致闪退
下面直接上代码:
OC端:
#import "ViewController.h" #import <WebKit/WebKit.h> // 定义需要注册的js消息事件handler名称 static NSString *const kScriptMessageHandler = @"ScriptMessageHandler"; @interface ViewController ()<WKUIDelegate,WKNavigationDelegate,WKScriptMessageHandler> // 遵循 WKScriptMessageHandler 协议 @property (nonatomic, strong) WKWebView *webView; @end @implementation ViewController - (void)viewDidLoad {
[super viewDidLoad]; // Do any additional setup after loading the view. WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config]; self.webView.UIDelegate = self; self.webView.navigationDelegate = self; NSString *URLString = @"https://"; NSURLRequest *testRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:URLString]]; [self.webView loadRequest:testRequest]; [self.view addSubview:self.webView]; } - (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated]; // 注册js事件 //这里给定一个消息事件的handler名称,在js端会对应的生成一个事件对象 [self.webView.configuration.userContentController addScriptMessageHandler:self name:kScriptMessageHandler]; } - (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated]; // 移除注册的js事件 [self.webView.configuration.userContentController removeScriptMessageHandlerForName:kScriptMessageHandler]; } #pragma mark - WKScriptMessageHandler // 实现 WKScriptMessageHandler 协议,接收js发送的时间回调 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
// 这里的message.name即我们注册过的 @"ScriptMessageHandler" // message.body是一个map,这个可以在js中自定义,传输任何key:value。 // 建议js端在body中有一个固定的字段,例如EventType,用来区分事件。这样的话我们只注册一个ScriptMessageHandler,实现多个不同事件的处理。减少代码,方便管理,同时减少js上的内存开销。不管是对客户端还是js端来说都是一个更加合理的解决方案。 NSLog(@"didReceiveScriptMessage %@ %@",message.name,message.body); if ([message.name isEqualToString:kScriptMessageHandler]) {
NSString *eventType =message.body[@"EventType"]; if ([eventType isEqualToString:@"EventType_ReLogin"]) {
// ... } ... } } @end
js端
if (window.webkit && window.webkit.messageHandlers.ScriptMessageHandler) {
window.webkit.messageHandlers.ScriptMessageHandler.postMessage({
Result: sucess, EventType: EventType_ReLogin });
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/199717.html原文链接:https://javaforall.net
