在上篇文章中講述了使用UIWebView攔截URL的方式來處理JS與OC互動。 由於UIWebView比較耗記憶體,效能上不太好,而蘋果在iOS 8中推出了WKWebView。 同樣的用WKWebView也可以攔截URL,做JS 與OC互動。關於WKWebView與UIWebView的對比,大家請自動百度或者google。
開啟百度網頁前 | 開啟百度網頁後 |
---|---|
UIWebView | 記憶體47M |
WKWebView | 記憶體47M |
WKWebView 攔截URL
WKWebView 與 UIWebView 攔截URL 的處理方式基本一樣。除了代理方法和WKWebView的使用不太一樣,關於WKWebView更詳盡的講解和用法,還是自行搜尋學習,本文重點還是講解如何實現JS 與OC 互相呼叫。
提醒:WKWebView 是iOS 8 推出的WebKit.framework中的控制元件,只有app 不需要相容iOS 7及以下的時候才可以使用。
先看動態效果圖:
1.建立WKWebView,載入本地HTML。
WKWebView的建立有幾點不同:
- 1.初始化多了個
configuration
引數,當然這個引數我們也可以不傳,直接使用預設的設定就好。 - 2.WKWebView的代理有兩個
navigationDelegate
和UIDelegate
。我們要攔截URL,就要通過navigationDelegate
的一個代理方法來實現。如果在HTML中要使用alert等彈窗,就必須得實現UIDelegate
的相應代理方法。 - 3.在iOS 9之前,WKWebView載入本地HTML會有一些問題。(不能載入本地HTML,或者部分CSS/本地圖片載入不了等)
我這裡建立WKWebView的示例程式碼是這樣的:
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
configuration.userContentController = [WKUserContentController new];
WKPreferences *preferences = [WKPreferences new];
preferences.javaScriptCanOpenWindowsAutomatically = YES;
preferences.minimumFontSize = 30.0;
configuration.preferences = preferences;
self.webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:configuration];
NSString *urlStr = [[NSBundle mainBundle] pathForResource:@"index.html" ofType:nil];
NSURL *fileURL = [NSURL fileURLWithPath:urlStr];
[self.webView loadFileURL:fileURL allowingReadAccessToURL:fileURL];
self.webView.navigationDelegate = self;
[self.view addSubview:self.webView];
複製程式碼
因為載入的本地HTML內容,跟上一篇UIWebView中介紹的HTML內容一樣,所以關於HTML中的內容就不再講解了。
2.攔截URL
使用WKNavigationDelegate中的代理方法,攔截自定義的URL來實現JS呼叫OC方法。
#pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
NSURL *URL = navigationAction.request.URL;
NSString *scheme = [URL scheme];
if ([scheme isEqualToString:@"haleyaction"]) {
[self handleCustomAction:URL];
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
decisionHandler(WKNavigationActionPolicyAllow);
}
複製程式碼
需要注意的是:
1.如果實現了這個代理方法,就必須得呼叫
decisionHandler
這個block,否則會導致app 崩潰。block引數是個列舉型別,WKNavigationActionPolicyCancel
代表取消載入,相當於UIWebView的代理方法return NO的情況;WKNavigationActionPolicyAllow
代表允許載入,相當於UIWebView的代理方法中 return YES的情況。
2.其他的關於為什麼要統一設定scheme,在上一篇中講過。
關於如何區分執行不同的OC 方法,也與UIWebView的處理方式一樣,通過URL 的host 來區分執行不同的方法:
#pragma mark - private method
- (void)handleCustomAction:(NSURL *)URL
{
NSString *host = [URL host];
if ([host isEqualToString:@"scanClick"]) {
NSLog(@"掃一掃");
} else if ([host isEqualToString:@"shareClick"]) {
[self share:URL];
} else if ([host isEqualToString:@"getLocation"]) {
[self getLocation];
} else if ([host isEqualToString:@"setColor"]) {
[self changeBGColor:URL];
} else if ([host isEqualToString:@"payAction"]) {
[self payAction:URL];
} else if ([host isEqualToString:@"shake"]) {
[self shakeAction];
} else if ([host isEqualToString:@"goBack"]) {
[self goBack];
}
}
複製程式碼
3.OC 呼叫 JS 方法
JS 呼叫OC 方法後,有的操作可能需要將結果返回給JS。這時候就是OC 呼叫JS 方法的場景。
WKWebView 提供了一個新的方法evaluateJavaScript:completionHandler:
,實現OC 呼叫JS 等場景。
- (void)getLocation
{
// 獲取位置資訊
// 將結果返回給js
NSString *jsStr = [NSString stringWithFormat:@"setLocation('%@')",@"廣東省深圳市南山區學府路XXXX號"];
[self.webView evaluateJavaScript:jsStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {
NSLog(@"%@----%@",result, error);
}];
}
複製程式碼
evaluateJavaScript:completionHandler:
沒有返回值,JS 執行成功還是失敗會在completionHandler 中返回。所以使用這個API 就可以避免執行耗時的JS,或者alert 導致介面卡住的問題。
4.WKWebView中使用彈窗
在上面提到,如果在WKWebView中使用alert、confirm 等彈窗,就得實現WKWebView的WKUIDelegate
中相應的代理方法。
例如,我在JS中要顯示alert 彈窗,就必須實現如下代理方法,否則alert 並不會彈出。
#pragma mark - WKUIDelegate
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提醒" message:message preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"知道了" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
completionHandler();
}]];
[self presentViewController:alert animated:YES completion:nil];
}
複製程式碼
其中completionHandler
這個block 一定得呼叫,至於在哪裡呼叫,倒是無所謂,我們也可以寫在方法實現的第一行,或者最後一行。
示例工程地址:JS_OC_URL