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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容