序言
在实际开发中,我们避免不了需要和 UIWebView 打交道,这就涉及到 JS 与原生 OC 的交互,今天抽空总结一下 JS 与原生交互使用 UIWebView 拦截 URL 的方式。
JS 调用原生 OC
我们可以利用 JS 发起一个假的 URL 请求,然后利用 UIWebView 的代理方法拦截这次请求,然后再做相应的处理。
参考网上例子,写了一个简单的 HTML 网页和一个按钮点击事件用来和原生 OC 交互,HTML代码如下:
<html>
<header>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript">
function showAlert(message){
asyncAlert(message);
}
function asyncAlert(content) {
setTimeout(function(){
alert(content);
},1);
}
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 firstClick() {
loadURL("firstClick://shareClick?title=分享的标题&content=分享的内容&url=链接地址&imagePath=图片地址");
}
</script>
</header>
<body style="width:100%; height:100%;">
<h2> 这里是第一种方式 </h2>
<br/>
<br/>
<button type="button" onclick="firstClick()">Click Me!</button>
</body>
</html>
虽然 HTML 内容比较少,还是有很多学问的
1.为什么定义一个
loadURL
方法,不直接使用window.location.href
?
答:因为如果当前网页正在使用window.location.href
加载网页的同时,调用window.location.href
去调用 OC 原生方法,会导致加载网页的操作被取消掉。同样的,如果连续使用window.location.href
执行两次 OC 原生调用,也有可能导致第一次的操作被取消掉。所以我们使用自定义的loadURL
,来避免这个问题。loadURL
的实现来自关于UIWebView和PhoneGap的总结一文。
2.为什么 loadURL 中的链接,使用统一的 scheme?
答:便于在 OC 中做拦截处理,减少在 JS 中调用一些 OC 没有实现的方法时,webView 做跳转。我再 OC 拦截 URL 时,根据 scheme即(firstclick
)来区分是调用原生的方法还是正常的网页跳转。然后根据host(即//后面的部分shareClick
)来区分执行什么操作。
3.为什么自定义一个asyncAlert
方法?
答:因为有的 JS 调用是需要 OC 返回结果到 JS 的。
stringByEvaluatingJavaScriptFromString
是一个同步方法,会等待 js 方法执行完成。而弹出的 alert 也会阻塞界面等待用户响应,所以他们可能会造成死锁。导致 alert 卡死界面。如果回调的 JS 是一个耗时操作,那么建议将耗时的操作也放入setTimeout
的function
中。
然后在项目的控制器中实现 UIWebView 的代理方法
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSURL * url = [request URL];
if ([[url scheme] isEqualToString:@"firstclick"]) { // firstClick://shareClick?title=分享的标题&content=分享的内容&url=链接地址&imagePath=图片地址
NSArray *params = [url.query componentsSeparatedByString:@"&"];
NSMutableDictionary *tempDict = [NSMutableDictionary dictionary];
NSMutableString *strM = [NSMutableString string];
for (NSString *paramStr in params) {
NSArray *dictArray = [paramStr componentsSeparatedByString:@"="];
if (dictArray.count > 1) {
NSString *decodeValue = [dictArray[1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
decodeValue = [decodeValue stringByRemovingPercentEncoding];
[tempDict setObject:decodeValue forKey:dictArray[0]];
[strM appendString:decodeValue];
}
}
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"这是OC原生的弹出窗" message:strM delegate:self cancelButtonTitle:@"收到" otherButtonTitles:nil];
[alertView show];
NSLog(@"tempDic:%@",tempDict);
return NO;
}
return YES;
}
1.JS中的firstClick,在拦截到的url scheme全都被转化为小写。
2.html 中需要设置编码,否则中文参数可能会出现编码问题。
3.JS用打开一个iFrame的方式替代直接用document.location的方式,以避免多次请求,被替换覆盖的问题。
OC 调用 JS
在 OC 原生中
NSString *jsStr = [NSString stringWithFormat:@"showAlert('%@')",@"这里是JS中alert弹出的message"];
[self.webView stringByEvaluatingJavaScriptFromString:jsStr];
注意:该方法会同步返回一个字符串,因此是一个同步方法,可能会阻塞主线程。
在 html 文件中
function showAlert(message) {
alert(message);
}
早期的JS与原生交互的开源库很多都是用得这种方式来实现的,例如:PhoneGap、WebViewJavascriptBridge。关于这种方式调用OC方法,唐巧早期有篇文章有过介绍:
关于UIWebView和PhoneGap的总结
效果如下:
js_oc_intercept_url.gif更多 JS 与 OC 交互文章请看下面
iOS下 JS 与OC 互相调用(二) - JavaScriptCore
iOS 下 JS 与 OC 互相调用(三) - WKWebView 拦截 URL
iOS下JS与OC互相调用(四)-MessageHandler
iOS下 JS 与 OC 互相调用(五) - UIWebView+WebViewJavascriptBridge
iOS下 JS 与 OC 互相调用(六) - WKWebView+WKWebViewJavascriptBridge
网友评论