原理:利用webview的代理方法(shouldStartLoadWithRequest),拦截即将加载的URL,再通过scheme区分点击事件类型。
先看看html页面的JS代码
function loadURL(url) { var iFrame; iFrame = document.createElement("iframe"); iFrame.setAttribute("src", url); iFrame.setAttribute("style", "display:none;"); iFrame.setAttribute("height", "0px"); iFrame.setAttribute("width", "0px"); iFrame.setAttribute("frameborder", "0"); document.body.appendChild(iFrame); // 发起请求后这个iFrame就没用了,所以把它从dom上移除掉 iFrame.parentNode.removeChild(iFrame); iFrame = null; } function scanClick() { alert(arr); //页面响应点击事件后使用iFrame的scr加载url,会被uiwebview的代理方法拦截 loadURL("haleyAction://scanClick"); } function shareClick() { loadURL("haleyAction://shareClick?title=测试分享的标题&content=测试分享的内容&url=http://www.baidu.com"); } function locationClick() { loadURL("haleyAction://getLocation"); } function setLocation(location) { //stringByEvaluatingJavaScriptFromString是一个同步方法,会等待js 方法执行完成,而弹出的alert 也会阻塞界面等待用户响应,所以他们可能会造成死锁。导致alert 卡死界面。如果回调的JS 是一个耗时的操作,那么建议将耗时的操作也放入setTimeout的function 中 asyncAlert(location); document.getElementById("returnValue").value = location; } function asyncAlert(content) { setTimeout(function(){ alert(content); },1); }
JS搞定了,那么下一步就是OC的操作了
主要用到-stringByEvaluatingJavaScriptFromString和webview的代理方法:-shouldStartLoadWithRequest
-stringByEvaluatingJavaScriptFromString——这个方法传入的就是JavaScript代码,直接在UIWebView上面执行js方法。
WKWebview中拦截URL的方法
与uiwebview不同之处:
初始化多了个configuration参数。 WKWebView的代理有两个navigationDelegate和UIDelegate。我们要拦截URL,就要通过navigationDelegate的一个代理方法来实现。如果在HTML中要使用alert等弹窗,就必须得实现UIDelegate的相应代理方法。 WKWebView的代理有两个navigationDelegate和UIDelegate。我们要拦截URL,就要通过navigationDelegate的一个代理方法来实现。如果在HTML中要使用alert等弹窗,就必须得实现UIDelegate的相应代理方法
初始化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];
拦截URL
使用WKNavigationDelegate中的代理方法,拦截自定义的URL来实现JS调用OC方法:
#pragma mark - WKNavigationDelegate - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler//如果实现了这个代理方法,就必须得调用decisionHandler这个block { NSURL *URL = navigationAction.request.URL; NSString *scheme = [URL scheme]; //uiwebview和wk都需要统一的scheme; if ([scheme isEqualToString:@"scheme"]) { [self handleCustomAction:URL]; //WKNavigationActionPolicyCancel代表取消加载等于是return NO; decisionHandler(WKNavigationActionPolicyCancel); return; } //WKNavigationActionPolicyAllow代表允许加载; decisionHandler(WKNavigationActionPolicyAllow); }
调用JS:
WKWebView 提供了一个新的方法evaluateJavaScript:completionHandler:,实现OC 调用JS 等场景。功能与stringByEvaluatingJavaScriptFromString类似。
WKWebView中使用alert:
在上面提到,如果在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) { //这个block 一定得调用,至于在哪里调用,倒是无所谓,我们也可以写在方法实现的第一行,或者最后一行 completionHandler(); }]]; [self presentViewController:alert animated:YES completion:nil]; }