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

最近准备把之前用UIWebView实现的JS与原生相互调用功能,用WKWebView来替换。顺便搜索整理了一下JS 与OC 交互的方式,非常之多啊。目前我已知的JS 与 OC 交互的处理方式: 

* 1.在JS 中做一次URL跳转,然后在OC中拦截跳转。(这里分为UIWebView 和 WKWebView两种,去年因为还要兼容iOS 6,所以没办法只能采用UIWebView来做。) 

* 2.利用WKWebView 的MessageHandler。 

* 3.利用系统库JavaScriptCore,来做相互调用。(iOS 7推出的) 

* 4.利用第三方库WebViewJavascriptBridge。 

* 5.利用第三方cordova库,以前叫PhoneGap。(这是一个库平台的库) 

* 6.当下盛行的React Native。

我去年也写过一个相互调用的总结:iOS下JS与原生OC互相调用(总结)。 

写的比较粗糙,因此准备新开一个目录专题来记录JS 与原生交互的处理方式。只是记录JS与OC交互的多种方式,大家可以根据实际情况和场景选择适合自己的方式。

今天就详细的介绍一下使用UIWebView拦截URL 的方式来实现JS与OC 的交互。 

为什么不使用第三方库或者RAC呢? 

因为就相互调用的接口使用的非常少啊,就那么三两个,完全没必要使用牛刀啊。

UIWebView 拦截URL

我之前就使用的是UIWebView + 拦截URL 的方式实现的JS与OC 交互。 

原因是因为要兼容iOS 6。

(一).创建UIWebView,并加载本地HTML。

加载本地HTML的目的是便于自己写JS调用做测试,最终肯定还是加载网络HTML。

self.webView = [[UIWebView alloc] initWithFrame:self.view.frame];

    self.webView.delegate = self;

    NSURL *htmlURL = [[NSBundle mainBundle] URLForResource:@"index.html" withExtension:nil];//    NSURL *htmlURL = [NSURL URLWithString:@"http://www.baidu.com"];    NSURLRequest *request = [NSURLRequest requestWithURL:htmlURL];


    // 如果不想要webView 的回弹效果    self.webView.scrollView.bounces = NO;

    // UIWebView 滚动的比较慢,这里设置为正常速度    self.webView.scrollView.decelerationRate = UIScrollViewDecelerationRateNormal;

    [self.webView loadRequest:request];

    [self.view addSubview:self.webView];

本地的HTML里,我定义了几个按钮,来触发调用原生的方法,然后再将执行结果回调到js 里。

// js 就列几个比较有代表性的functions:functionloadURL(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;

}functionasyncAlert(content) {

    setTimeout(function(){

               alert(content);

               },1);

}functionlocationClick() {

    loadURL("haleyAction://getLocation");

}

            functionsetLocation(location) {

    asyncAlert(location);

    document.getElementById("returnValue").value = location;

}

虽然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 (即haleyAction)来区分是调用原生的方法还是正常的网页跳转。然后根据host(即//后的部分getLocation)来区分执行什么操作。 

3.为什么自定义一个asyncAlert方法? 

答:因为有的JS调用是需要OC 返回结果到JS的。stringByEvaluatingJavaScriptFromString是一个同步方法,会等待js 方法执行完成,而弹出的alert 也会阻塞界面等待用户响应,所以他们可能会造成死锁。导致alert 卡死界面。如果回调的JS 是一个耗时的操作,那么建议将耗时的操作也放入setTimeout的function 中。

(二).拦截 URL

UIWebView 有一个代理方法,可以拦截到每一个链接的Request。return YES,webView 就会加载这个链接;return NO,webView 就不会加载这个连接。我们就在这个拦截的代理方法中处理自己的URL。 

这是我的示例代码:

#pragma mark - UIWebViewDelegate- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType

{

    NSURL *URL = request.URL;

    NSString *scheme = [URL scheme];

    if ([scheme isEqualToString:@"haleyaction"]) {

        [self handleCustomAction:URL];

        return NO;

    }

    return YES;

}

这里通过scheme,来拦截掉自定义的URL 就非常容易了,如果不同的方法使用不同的scheme,那么判断起来就非常的麻烦。 

然后,看看我的处理连接的方法:

#pragma mark - private method- (void)handleCustomAction:(NSURL *)URL

{

    NSString *host = [URL host];

    if ([host isEqualToString:@"scanClick"]) {

        NSLog(@"扫一扫");

    } else if ([host isEqualToString:@"shareClick"]) {

        [self share:URL];

    } else if ([host isEqualToString:@"getLocation"]) {

        [self getLocation];

    } else if ([host isEqualToString:@"setColor"]) {

        [self changeBGColor:URL];

    } else if ([host isEqualToString:@"payAction"]) {

        [self payAction:URL];

    } else if ([host isEqualToString:@"shake"]) {

        [self shakeAction];

    } else if ([host isEqualToString:@"goBack"]) {

        [self goBack];

    }

}

顺便看一下如何将结果回调到JS中:

- (void)getLocation

{

    // 获取位置信息    

    // 将结果返回给js    NSString *jsStr = [NSString stringWithFormat:@"setLocation('%@')",@"广东省深圳市南山区学府路XXXX号"];

    [self.webView stringByEvaluatingJavaScriptFromString:jsStr];

}

当然,有时候我们在JS中调用OC 方法的时候,也需要传参数到OC 中,怎么传呢? 

就像一个get 请求一样,把参数放在后面:

functionshareClick(){ loadURL("haleyAction://shareClick?title=测试分享的标题&content=测试分享的内容&url=http://www.baidu.com");

}

那么如果获取到这些参数呢? 

所有的参数都在URL的query中,先通过&将字符串拆分,在通过=把参数拆分成key 和实际的值。下面有示例代码:

- (void)share:(NSURL *)URL

{

    NSArray *params =[URL.query componentsSeparatedByString:@"&"];


    NSMutableDictionary *tempDic = [NSMutableDictionary dictionary];

    for (NSString *paramStr in params) {

        NSArray *dicArray = [paramStr componentsSeparatedByString:@"="];

        if (dicArray.count > 1) {

            NSString *decodeValue = [dicArray[1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

            [tempDic setObject:decodeValue forKey:dicArray[0]];

        }

    }


    NSString *title = [tempDic objectForKey:@"title"];

    NSString *content = [tempDic objectForKey:@"content"];

    NSString *url = [tempDic objectForKey:@"url"];

    // 在这里执行分享的操作    

    // 将分享结果返回给js    NSString *jsStr = [NSString stringWithFormat:@"shareResult('%@','%@','%@')",title,content,url];

    [self.webView stringByEvaluatingJavaScriptFromString:jsStr];

}

(三). OC调用JS方法

关于将OC 执行结果返回给JS 需要注意的是:如果回调执行的JS 方法带参数,而参数不是字符串时,不要加单引号,否则可能导致调用JS 方法失败。比如我这样的:

NSData *jsonData = [NSJSONSerialization dataWithJSONObject:userProfile options:NSJSONWritingPrettyPrinted error:nil];NSString *jsonStr = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];NSString *jsStr = [NSString stringWithFormat:@"loginResult('%@',%@)",type, jsonStr];

[_webView stringByEvaluatingJavaScriptFromString:jsStr];

如果第二个参数用单引号包起来,就会导致JS端的loginResult不会调用。

示例工程地址:JS_OC_URL

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/u011619283/article/details/52135977

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

推荐阅读更多精彩内容

  • 随着H5技术的兴起,在iOS开发过程中,难免会遇到原生应用需要和H5页面交互的问题。其中会涉及方法调用及参数传值等...
    Chris_js阅读 3,051评论 1 8
  • WebViewJavaScriptBridge是IOS中JS和OC交互的常用框架,支持以下两种调用: 1. OC端...
    lim2阅读 664评论 0 0
  • 目录 《App与Js交互(一)iOS》 《App与Js交互(二)Android》 《App与Js交互(三)Andr...
    gwpp阅读 10,331评论 1 20
  • 古有人生四大乐事之说:久旱逢甘雨,他乡遇故知,洞房花烛夜,金榜提名时。 苏东坡则认为,人生赏心乐事很多,不单只有四...
    阳光206阅读 587评论 0 1
  • 春日早尽有感 简静姝 斜阳草树茵磎戏, 梨落晶莹雪满地。 芳菲歇尽柳残红, 还似明年今日意。 王孙早谢林寻觅, 浣...
    姜清谈阅读 282评论 13 5