文档章节

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

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

一、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.7K
22
开发一个移动跨平台库 —— 第一部分:探索

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

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

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

瑾少
2018/04/14
359
3
Objective-C 解释器 - OCEval

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

soyoo
01/08
0
0
10 个易于开发的移动应用开发框架

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

红薯
2011/09/28
7.8K
6

没有更多内容

加载失败,请刷新页面

加载更多

SpringBoot引入第三方jar包或本地jar包的处理方式

在开发过程中有时会用到maven仓库里没有的jar包或者本地的jar包,这时没办法通过pom直接引入,那么该怎么解决呢 一般有两种方法 - 第一种是将本地jar包安装在本地maven库 - 第二种是将本地j...

独钓渔
今天
2
0
五、MyBatis缓存

一、MyBatis缓存介绍 缓存的使用可以明显的加快访问数据速度,提升程序处理性能,生活和工作中,使用缓存的地方很多。在开发过程中,从前端-->后端-->数据库等都涉及到缓存。MyBatis作为数据...

yangjianzhou
今天
2
0
最近研究如何加速UI界面开发,有点感觉了

最近在开发JFinal学院的JBolt开发平台,后端没啥说的,做各种极简使用的封装,开发者上手直接使用。 JBolt开发平台包含常用的用户、角色、权限、字典、全局配置、缓存、增删改查完整模块、电...

山东-小木
今天
3
0
《月亮与六便士》的读后感作文3000字

《月亮与六便士》的读后感作文3000字: 看完英国作家威廉.萨默塞特.毛姆所著《月亮与六便士》(李继宏译),第一疑问就是全书即没提到“月亮”,也没提到“六便士”。那这书名又与内容有什么...

原创小博客
昨天
2
0
微信网页授权获取用户信息(ThinkPHP5)+ 微信发送客服消息(一)

以thinkphp5为实例,创建控制器 class Kf extends Controller { /** * [protected description]微信公众号appid * @var [type] */ protected $appid = "xxxxxxxxxxxxxxx"; /** * [protected......

半缘修道半缘君丶
昨天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部