原理:利用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];
}