iOS中JS与原生OC互相调用之问题总结二

来源:互联网 发布:樱井知香番号合集 编辑:程序博客网 时间:2024/06/05 07:18

今天继续给同学们讲解JS和OC的互相调用,今天给大家还是讲解WKWebView中的一些使用,和注意点,废话不多说直接上代码:


#pragma mark - WKWebView中的MessageHandler?

WKWebView初始化时,有一个参数叫configuration,它是WKWebViewConfiguration类型的参数,而WKWebViewConfiguration有一个属性叫userContentController,它又是WKUserContentController类型的参数。WKUserContentController对象有一个方法

- addScriptMessageHandler:name:我把这个功能简称为MessageHandler。

- addScriptMessageHandler:name:有两个参数,第一个参数是userContentController的代理对象,第二个参数是JS里发送postMessage的对象(即你与前端JS定义好的方法名)。


#warning - 注意

所以要使用MessageHandler功能,就必须要实现WKScriptMessageHandler协议。


#pragma mark - <WebKit/WebKit.h>API中对JS的使用方法介绍?

我们在该API的描述里可以看到在JS中的使用方法:

window.webkit.messageHandlers.<name>.postMessage(<messageBody>) 


// 其中<name>,就是上面方法里的第二个参数'name';例:我们调用API的时候第二个参数填@"Share",那么在JS里就是: window.webkit.messageHandlers.Share.postMessage(<messageBody>)


// <messageBody>是一个键值对,键是body,值可以有多种类型的参数。

// 在'WKScriptMessageHandler'协议中,我们可以看到message是'WKScriptMessage'类型,有一个属性叫body

// 而注释里写明了body 的类型:Allowed types are NSNumber, NSString, NSDate, NSArray, NSDictionary, and NSNull.


#pragma mark - 那么问题来了,如何使用MessageHandler?

1> 创建WKWebViewConfiguration对象,配置各个API对应的MessageHandler,WKUserContentController对象可以添加多个scriptMessageHandler。示例代码:

/**

 *  创建一个WKWebViewConfiguration对象

 */

- (void)creatConfiguration

{

    WKWebViewConfiguration *configuration = [[WKWebViewConfigurationalloc] init];

    WKPreferences *preference = [[WKPreferencesalloc] init];// 对一个webView的属性设置

    preference.javaScriptCanOpenWindowsAutomatically =YES;

    configuration.preferences = preference;

}

#pragma mark - 一般在这里就设置整个viewwebView

- (void)loadView

{

    [superloadView];

    

    self.view =self.webView;

}


#pragma mark - view即将出现的时候我们就可以监听

- (void)viewWillAppear:(BOOL)animated

{

    [superviewWillAppear:animated];

    

    // addScriptMessageHandler 很容易导致循环引用

    // 控制器强引用了WKWebView,WKWebView copy(强引用了)configuration,configuration copy(强引用了)userContentController

    // userContentController 强引用了 self(控制器)

    [self.webView.configuration.userContentControlleraddScriptMessageHandler:selfname:@"ScanAction"];

    [self.webView.configuration.userContentControlleraddScriptMessageHandler:selfname:@"Location"];

    [self.webView.configuration.userContentControlleraddScriptMessageHandler:selfname:@"Share"];

    [self.webView.configuration.userContentControlleraddScriptMessageHandler:selfname:@"Color"];

    [self.webView.configuration.userContentControlleraddScriptMessageHandler:selfname:@"Pay"];

    [self.webView.configuration.userContentControlleraddScriptMessageHandler:selfname:@"Shake"];

    [self.webView.configuration.userContentControlleraddScriptMessageHandler:selfname:@"GoBack"];

    [self.webView.configuration.userContentControlleraddScriptMessageHandler:selfname:@"PlaySound"];

}


#warning - 需要注意的是addScriptMessageHandler很容易引起循环引用,导致控制器无法被释放,所以需要加入以下这段

- (void)viewWillDisappear:(BOOL)animated

{

    [superviewWillDisappear:animated];

    

    // 因此这里要记得移除handlers

    [self.webView.configuration.userContentControllerremoveScriptMessageHandlerForName:@"ScanAction"];

    [self.webView.configuration.userContentControllerremoveScriptMessageHandlerForName:@"Location"];

    [self.webView.configuration.userContentControllerremoveScriptMessageHandlerForName:@"Share"];

    [self.webView.configuration.userContentControllerremoveScriptMessageHandlerForName:@"Color"];

    [self.webView.configuration.userContentControllerremoveScriptMessageHandlerForName:@"Pay"];

    [self.webView.configuration.userContentControllerremoveScriptMessageHandlerForName:@"Shake"];

    [self.webView.configuration.userContentControllerremoveScriptMessageHandlerForName:@"GoBack"];

    [self.webView.configuration.userContentControllerremoveScriptMessageHandlerForName:@"PlaySound"];

}


#pragma mark - 创建WKWebView?

- (void)viewDidLoad {

    [superviewDidLoad];

    

    // 0.创建WKWebView

    [selfsetUpWebView];

    

    // 1.创建WKWebViewConfiguration对象

    [selfsetUpConfiguration];

}


/**

 *  创建webView

 */

- (void)setUpWebView

{

    self.webView = [[WKWebViewalloc] initWithFrame:self.view.frameconfiguration:nil];

    NSString *urlStr = [[NSBundlemainBundle] pathForResource:@"test.html"ofType:nil];

    NSURL *fileURL = [NSURLfileURLWithPath:urlStr];

    [self.webViewloadFileURL:fileURL allowingReadAccessToURL:fileURL];

    

    self.webView.navigationDelegate = self;

    self.webView.UIDelegate =self;

    [self.viewaddSubview:self.webView];

}


/**

 *  创建一个WKWebViewConfiguration对象

 */

- (void)setUpConfiguration

{

    WKWebViewConfiguration *configuration = [[WKWebViewConfigurationalloc] init];

    WKPreferences *preference = [[WKPreferencesalloc] init];// 对一个webView的属性设置

    preference.javaScriptCanOpenWindowsAutomatically =YES;

    configuration.preferences = preference;

}


#pragma mark - 实现协议方法

#pragma mark - 我这里实现了两个协议<WKUIDelegate,WKScriptMessageHandler>,WKUIDelegate是因为我在JS中弹出了alertWKScriptMessageHandler是因为我们要处理JS调用OC方法的请求。

#pragma mark - WKScriptMessageHandler

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message

{

    //  message.body -- Allowed types are NSNumber, NSString, NSDate, NSArray,NSDictionary, and NSNull.

    if ([message.nameisEqualToString:@"ScanAction"]) {

        NSLog(@"扫一扫");

    } elseif ([message.nameisEqualToString:@"Location"]) {

        [selftest];

    } elseif ([message.nameisEqualToString:@"Share"]) {

        [selftest];

    } elseif ([message.nameisEqualToString:@"Color"]) {

        [selftest];

    } elseif ([message.nameisEqualToString:@"Pay"]) {

        [selftest];

    } elseif ([message.nameisEqualToString:@"Shake"]) {

        [selftest];

    } elseif ([message.nameisEqualToString:@"GoBack"]) {

        [selftest];

    } elseif ([message.nameisEqualToString:@"PlaySound"]) {

        [selftest];

    }

}


- (void)test

{

    

}


#pragma mark - WKScriptMessage有两个关键属性namebody。因为我们给每一个OC方法取了一个name,那么我们就可以根据name来区分执行不同的方法。body中存着JS要给OC传的参数。关于参数body的解析,我就举一个body中放字典的例子.

/**

 *  @param tempDic tempDic来自message.body来自与JS传过来的参数

 */

- (void)shareWithParams:(NSDictionary *)tempDic

{

    if (![tempDicisKindOfClass:[NSDictionaryclass]]) return;

    

    // kvc取值

    NSString *title = [tempDicobjectForKey:@"title"];

    NSString *content = [tempDicobjectForKey:@"content"];

    NSString *url = [tempDicobjectForKey:@"url"];

    

    // 在这里执行分享的操作

    

#pragma mark - OC调用JS

    // 将分享结果返回给js:OC调用JS

    NSString *jsStr = [NSStringstringWithFormat:@"shareResult('%@','%@','%@')",title,content,url];

    [self.webViewevaluateJavaScript:jsStrcompletionHandler:^(id_Nullable result, NSError * _Nullable error) {

        NSLog(@"%@----%@",result, error);

    }];

}


#pragma mark - 那么这里总结用MessageHandler的好处

/* 

 * 1> JS中写起来简单,不用再用创建URL的方式那么麻烦了。

 * 2> JS传递参数更方便。使用拦截URL的方式传递参数,只能把参数拼接在后面,如果遇到要传递的参数中有特殊字符,如&=、?等,必须得转换,否则参数解析肯定会出错。

 * 例如传递的url是这样的:

 * http://www.baidu.com/share/openShare.htm?share_uuid=shdfxdfdsfsdf&name=1234567890

 * 使用拦截URLJS调用方式

 * loadURL("firstClick://shareClick?title=分享的标题&content=分享的内容&url=链接地址&imagePath=图片地址"); }

 * 将上面的url放入链接地址这里后,根本无法区分share_uuid是其他参数,还是url里附带的参数。

 * 但是使用MessageHandler就可以避免特殊字符引起的问题。

 */






原创粉丝点击