文档章节

【iOS & Web】JavaScript & Objective-C二重奏

魔笛GNR
 魔笛GNR
发布于 2016/10/19 18:12
字数 1700
阅读 42
收藏 1

一、JS call OC

方法1:

通拦截协议头来获取协议字符串。在UIWebView中的代理方法中有这样的方法,如下图所示:

//UIWebView每次请求内容之前,都会调用这个方法,通过返回YES/NO来决定UIWebView是否进行request请求。
//我们可以通过URL的协议头及字符串来区别普通的URL请求
//JS传递给OC的参数可以通过URL带过来,如果参数内容过长可以通过post请求来传递,本地在拦截request后,可以将HTTPBody中的请求内容解析出来。
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    return YES;
}

下面是我写的简单的H5页面通过JS请求一个自定义协议的URL,然后通过UIWebView来拦截:

Demo1:

第一种: 无参数的协议URL

H5和JS代码如下:

<!-- H5代码 -->
	<input type="button" onclick="shareWexin()" value="无参协议的URL">

	<!-- JS 代码 -->
	<script type="text/javascript">
		function shareWexin() {
			//这里objc是协议头,作为iOS端拦截的标识
			//shareWX即为‘协议名’、作为iOS方法的识别
			window.location.href="objc://shareWX";
		}
	</script>

UIWebView代理中的协议拦截:

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    
    NSString *urlString = [[request URL] absoluteString];
    
    NSLog(@"协议URL => %@",urlString);
    
    NSArray *urlComps = [urlString componentsSeparatedByString:@"://"];
    
    if([urlComps count] && [[urlComps objectAtIndex:0] isEqualToString:@"objc"]){
        
        NSLog(@"方法名 => %@",urlComps.lastObject);
        
        return NO;
    }
    return YES;
}

点击网页中的按钮打印如下所示:

现在我们成功拦截到了协议,后续的处理就不用我多说了吧!

第二种: 有参数的协议URL

H5和JS代码如下:

<body>
	<h2>第一种方法</h2>
	<h4>请求有参协议的URL</h4>
	<!-- H5代码 -->
	<input type="button" onclick="shareQQ()" value="有参协议的URL">

	<!-- JS 代码 -->
	<script type="text/javascript">
		function shareQQ() {
			//这里objc是协议头,作为iOS端拦截的标识
			//shareWX即为‘协议名’、作为iOS方法的识别
			//type=qq 即为参数
			window.location.href="objc://shareQQ?type=qq";
		}
	</script>
</body>

UIWebView代理中的协议拦截:

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    
    NSString *urlString = [[request URL] absoluteString];
    
    NSLog(@"协议URL => %@",urlString);
    
    NSArray *urlComps = [urlString componentsSeparatedByString:@"://"];
    
    if([urlComps count] && [[urlComps objectAtIndex:0] isEqualToString:@"objc"]){
        
        NSLog(@"方法名及参数URL => %@",urlComps.lastObject);
        
        NSString * parmeterURL = [urlComps lastObject];

        //获取参数
        NSArray * parmeters = [parmeterURL componentsSeparatedByString:@"?"];
        
        NSString * parStr = [parmeters lastObject];
        
        NSLog(@"携带参数的字符串 => %@",parStr);
        
        return NO;
    }
    return YES;
}

点击网页中的按钮打印如下所示:

现在我们成功拦截到了协议中的方法名和参数,这样你就可以进行后续的处理了!

问题:

这种方法主要是通过JS调用一个我们自己的通用协议URL,iOS端通过拦截的方法,来达到JS调用OC的目的,但是这存在不少问题,比如JS如果连续请求两次以上协议,iOS端只能拦截最后一个,这样就无法真正的做到JS调用OC;另外通过拦截的方法的在执行效率上比较慢!

那么还有更好的方法么?当然这就需要我们用到JavaScriptCore来实现了,一下是第二种方法:

 

方法2:

通过注入JS代码来消除JS异常情况,并实现上下文的Block回调,来实现调用OC的目的,下面为具体的步骤:

一、引入JavaScriptCore

@import JavaScriptCore;

二、在页面加载完成后,获取JS上下文:

//获取JS上下文
    JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

三、注入JS代码来触发block:

//定义好JS要调用的方法,如shareWB
    context[@"shareWB"] = ^() {
        
        // your code
    };

Demo2:

第一种: 无参数的方法

通过JS调用和iOS端协商好的方法,在JS中并未实现,所以在JS中属于异常的情况,下面为H5和JS代码:

<h2>第二种方法</h2>
<input type="button" onclick="shareWebo()" value="第二种方法">

<script type="text/javascript">
    function shareWebo() {
        //这是和iOS端协商好的方法,在JS中并未实现,所以在JS中属于异常的情况
	    shareWB();
    }
</script>

捕获异常,即注入JS代码使消除异常情况,OC主要代码:

- (void)webViewDidFinishLoad:(UIWebView *)webView{
    NSLog(@"页面加载完成");
    
    //获取JS上下文
    JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    //定义好JS要调用的方法,如shareWB
    context[@"shareWB"] = ^() {
        
        NSLog(@"shareWB 我被调用了!");
        
        NSArray *args = [JSContext currentArguments];

        for (JSValue *jsVal in args) {
            NSLog(@"%@", jsVal.toString);
        }
        
    };
}

点击网页中的按钮打印如下所示:

第二种: 有参数的方法

下面为H5和JS代码:

<h4>有参数的方法</h4>
<input type="button" onclick="shareMessage()" value="有参数的方法">

<script type="text/javascript">
	function shareMessage(){
		//带参数的 未实现的方法, JS中同样属于异常的情况
		shareMsg("p1","p2");
	}
</script>

OC主要代码:

- (void)webViewDidFinishLoad:(UIWebView *)webView{
    NSLog(@"页面加载完成");
    
    //获取JS上下文
    JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

    context[@"shareMsg"] = ^() {
        
        NSLog(@"shareMsg 我被调用了!");
        
        NSArray *args = [JSContext currentArguments];
        
        for (JSValue *jsVal in args) {
            NSLog(@"参数 => %@", jsVal.toString);
        }
        
    };
    
}

点击网页中的按钮打印如下所示:

问题:

可以同时触发多个事件么?以下是测试结果:

在JS中调用两个方法:

<script type="text/javascript">

		function shareMessage(){
			//带参数的 未实现的方法, JS中同样属于异常的情况
			shareWB();
			shareMsg("p1","p2");
		}
	</script>

OC中的打印结果:

经测试,答案是肯定的!

 

下面我们探讨一下OC Call JS:

二、OC Call JS

方法一:

通过向UIWebView注入JS方法来调用JS内部的方法,如下所示:

- (IBAction)callJS:(id)sender {
    //调用js方法的字符串
    NSString *jsStr = [NSString stringWithFormat:@"alertMsg('%@')",@"提示的信息"];
    
    //将js注入webView
    [self.webView stringByEvaluatingJavaScriptFromString:jsStr];
}

以下是JS主要代码:

	<script type="text/javascript">
		//被oc调用的方法
		function alertMsg(msg) {
			alert(msg);
		}
	</script>

以下是运行的效果,看这个弹出可以JS调用的哦:

方法二:

使用JavaScriptCore来和JS交互

- (IBAction)callJS:(id)sender {
    //获取JS上下文
    JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    
    //JS代码字符串
    NSString *JS = @"alertMsg('提示信息2')";
    [context evaluateScript:JS];
}

以下是JS主要代码:

	<script type="text/javascript">
		//被oc调用的方法
		function alertMsg(msg) {
			alert(msg);
		}
	</script>

以下是运行的效果,看这个弹出可以JS调用的哦:

注意:

以上两种方法都是同步方法,如果JS比较耗时,会造成界面卡顿,建议将JS耗时的程序放在异步线程中执行!比如alert()方法就会阻塞UI主线程(注意:按钮‘注入JS’此时为高亮状态,说明弹窗阻塞了主线程,在等待用户响应),我们可以通过setTimeout()方法来实现异步执行的目的,如下所示:

	<script type="text/javascript">
        //被oc调用的方法
		function alertMsg(msg) {
            //异步执行
			setTimeout(function(){
         		alert(msg);
         	},1);
		}
	</script>

以下为运行结果,‘注入JS’按钮恢复了正常状态,说明此时alert()是异步执行的。


© 著作权归作者所有

魔笛GNR
粉丝 7
博文 82
码字总数 43485
作品 0
郑州
iOS工程师
私信 提问
#每日播报# 8月6日 Github 热门项目汇总

这是一份来自美国的网友利用 Github 的 API 获取的每日 Github 上热门项目列表,该网友每天都会发布更新列表。下面是 8月6日的热门项目列表: objective-c ChristianKienle/Core-Data-Editor...

oschina
2014/08/08
6.8K
22
开发一个移动跨平台库 —— 第一部分:探索

本文中,我介绍了我在探索开发移动跨平台库(例如一个codebase,可成为不同移动平台上的app的一部分)时积累的经验:从移动跨平台开发工具(PhoneGap,Titanium之类的),到代码移植工具;从无法...

oschina
2014/03/01
4.2K
12
目前有成熟的App代码翻译技术吗?

大家好: 本人多年技术老鸟,现有抛出一个思考。针对目前市面上的移动互联网项目(主要指客户端)类型,无非以下几种主要类型: 原生App: iOS/Android H5响应式 H5套壳(PhoneGap/微信小程序/国...

瑾少
2018/04/14
580
3
10 个易于开发的移动应用开发框架

由于 iPhone 和谷歌Android 推出移动应用开发正在迅速增长。有无数的移动Web应用程序在互联网上公布,这些应用程序在发布之前都需要经过大量的工作和很多工程师辛勤的劳动,开发移动应用并不...

红薯
2011/09/28
7.8K
6
Objective-C 解释器 - OCEval

目前流行的 JSPatch/RN 基于JavaScriptCore提供了iOS的热修复和动态化方案。但是都必须通过下发Javascript脚本来调用Objective-C。 尤其是JSPatch,编写大量的JS代码来调用OC的方法,开发效率...

soyoo
01/08
235
0

没有更多内容

加载失败,请刷新页面

加载更多

CSS--列表

一、列表标识项 list-style-type none:去掉标识项 disc:默认实心圆 circle:空心圆 squire:矩形 二、列表项图片 list-style-img: 取值:url(路径) 三、列表项位置 list-style-position:...

wytao1995
今天
6
0
linux 命令-文本比较comm、diff、patch

本文原创首发于公众号:编程三分钟 今天学了三个文本比较的命令分享给大家。 comm comm 命令比较相同的文本 $ cat charabc$ cat chardiffadc 比如,我有两个文件char和chardiff如上,...

编程三分钟
今天
7
0
QML教程

https://blog.csdn.net/qq_40194498/article/category/7580030 https://blog.csdn.net/LaineGates/article/details/50887765...

shzwork
今天
5
0
HA Cluster之5

对于使用heartbeat v2版的CRM配置的集群信息都是保存在一个名为cib.xml的配置文件中,存放在/var/lib/heartbeat/crm/下。CIB:Cluster Information Base,由于xml文件配置不是那么方便,所以...

lhdzw
今天
6
0
玩转Redis-Redis基础数据结构及核心命令

  《玩转Redis》系列文章主要讲述Redis的基础及中高级应用,文章基于Redis5.0.4+。本文主要讲述Redis的数据结构String,《玩转Redis-Redis基础数据结构及核心命令》相关操作命令为方便对比...

zxiaofan666
今天
11
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部