iOS開發-JS與原生OC互相呼叫之問題總結二
今天繼續給同學們講解JS和OC的互相呼叫,今天給大家還是講解WKWebView中的一些使用,和注意點,廢話不多說直接上程式碼:
#pragma mark - WKWebView中的MessageHandler?
WKWebView初始化時,有一個引數叫configuration,它是WKWebViewConfiguration
型別的引數,而WKWebViewConfiguration
有一個屬性叫userContentController
,它又是WKUserContentController
型別的引數。WKUserContentController
物件有一個方法
- addScriptMessageHandler:name:
- 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 = [[WKPreferences alloc] init]; // 對一個webView的屬性設定
preference.javaScriptCanOpenWindowsAutomatically = YES;
configuration.preferences = preference;
}
#pragma mark - 一般在這裡就設定整個view是webView
- (void)loadView
{
[super loadView];
self.view = self.webView;
}
#pragma mark - view即將出現的時候我們就可以監聽
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear: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
{
[super viewWillDisappear: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 = [NSURL fileURLWithPath:urlStr];
[self.webViewloadFileURL:fileURL allowingReadAccessToURL:fileURL];
self.webView.navigationDelegate = self;
self.webView.UIDelegate = self;
[self.view addSubview:self.webView];
}
/**
* 建立一個WKWebViewConfiguration物件
*/
- (void)setUpConfiguration
{
WKWebViewConfiguration *configuration = [[WKWebViewConfigurationalloc] init];
WKPreferences *preference = [[WKPreferences alloc] init]; // 對一個webView的屬性設定
preference.javaScriptCanOpenWindowsAutomatically = YES;
configuration.preferences = preference;
}
#pragma mark - 實現協議方法
#pragma mark - 我這裡實現了兩個協議<WKUIDelegate,WKScriptMessageHandler>,WKUIDelegate是因為我在JS中彈出了alert。WKScriptMessageHandler是因為我們要處理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.name isEqualToString:@"ScanAction"]) {
NSLog(@"掃一掃");
} else if ([message.name isEqualToString:@"Location"]) {
[self test];
} else if ([message.name isEqualToString:@"Share"]) {
[self test];
} else if ([message.name isEqualToString:@"Color"]) {
[self test];
} else if ([message.name isEqualToString:@"Pay"]) {
[self test];
} else if ([message.name isEqualToString:@"Shake"]) {
[self test];
} else if ([message.name isEqualToString:@"GoBack"]) {
[self test];
} else if ([message.name isEqualToString:@"PlaySound"]) {
[self test];
}
}
- (void)test
{
}
#pragma mark - WKScriptMessage有兩個關鍵屬性name和body。因為我們給每一個OC方法取了一個name,那麼我們就可以根據name 來區分執行不同的方法。body中存著JS要給OC傳的引數。關於引數body的解析,我就舉一個body中放字典的例子.
/**
* @param tempDic tempDic來自message.body來自與JS傳過來的引數
*/
- (void)shareWithParams:(NSDictionary *)tempDic
{
if (![tempDic isKindOfClass:[NSDictionary class]]) return;
// kvc取值
NSString *title = [tempDic objectForKey:@"title"];
NSString *content = [tempDic objectForKey:@"content"];
NSString *url = [tempDic objectForKey:@"url"];
// 在這裡執行分享的操作
#pragma mark - OC呼叫JS
// 將分享結果返回給js 即:OC呼叫JS
NSString *jsStr = [NSString stringWithFormat:@"shareResult('%@','%@','%@')",title,content,url];
[self.webView evaluateJavaScript:jsStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {
NSLog(@"%@----%@",result, error);
}];
}
#pragma mark - 那麼這裡總結用MessageHandler的好處
/*
* 1> 在JS中寫起來簡單,不用再用建立URL的方式那麼麻煩了。
* 2> JS傳遞引數更方便。使用攔截URL的方式傳遞引數,只能把引數拼接在後面,如果遇到要傳遞的引數中有特殊字元,如&、=、?等,必須得轉換,否則引數解析肯定會出錯。
* 例如傳遞的url是這樣的:
* 使用攔截URL 的JS呼叫方式
* loadURL("firstClick://shareClick?title=分享的標題&content=分享的內容&url=連結地址&imagePath=圖片地址"); }
* 將上面的url 放入連結地址這裡後,根本無法區分share_uuid是其他引數,還是url裡附帶的引數。
* 但是使用MessageHandler 就可以避免特殊字元引起的問題。
*/