1. 程式人生 > >iOS開發-JS與原生OC互相呼叫之問題總結二

iOS開發-JS與原生OC互相呼叫之問題總結二

今天繼續給同學們講解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 = [[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 就可以避免特殊字元引起的問題。

 */