iOS下 JS 与OC 互相调用(一) - UIWebView 拦截 URL

序言

在实际开发中,我们避免不了需要和 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 是一个耗时操作,那么建议将耗时的操作也放入setTimeoutfunction中。

然后在项目的控制器中实现 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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容