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

1、JS call OC

方法1:

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

//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来拦截:html

Demo1:

第一种: 无参数的协议URL

H5和JS代码以下:java

<!-- 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代理中的协议拦截:web

- (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;
}

点击网页中的按钮打印以下所示:异步

如今咱们成功拦截到了协议,后续的处理就不用我多说了吧!post

第二种: 有参数的协议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代理中的协议拦截:lua

- (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;
}

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

如今咱们成功拦截到了协议中的方法名和参数,这样你就能够进行后续的处理了!spa

问题:

这种方法主要是经过JS调用一个咱们本身的通用协议URL,iOS端经过拦截的方法,来达到JS调用OC的目的,可是这存在很多问题,好比JS若是连续请求两次以上协议,iOS端只能拦截最后一个,这样就没法真正的作到JS调用OC;另外经过拦截的方法的在执行效率上比较慢!

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

 

方法2:

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

1、引入JavaScriptCore

@import JavaScriptCore;

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

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

3、注入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:

2、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()是异步执行的。


相关文章
相关标签/搜索