现在越来越多的APP都是H5和原生混合开发,这样确实方便快捷,但是H5的部分总避免不了很多与原生的交互,原生调JS函数还比较简单,原生的API函数stringByEvaluatingJavaScriptFromString就可以完成需求,但是JS调原生的函数,系统没有提供API,所以很多人公司都采用标记位的形式完成,我们公司的也不例外,项目开发了很久,原生和JS交互一直是我负责,我们项目这块也要大量的交互操作,随便版本迭代,问题也越来越多,然后就想了很多办法去解决这个问题。

一,我原本的方案,拦截URL完成

拦截URL的方式就是在如下方法中拦截抛出的URL,URL中包含关键的标识字段,根据这些字段调用原生的函数,这样H5中的相应操作全部可以抛出包含关键字段的URL,然后原生根据各自的字段采取不同的操作,完成H5和原生的交互.

-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType

返回YES的话Webview继续加载新的URL,返回NO就不加载,这样的话凡是需要采取操作的URL都可以返回NO,让webview不再加载,只调用原生的函数就行。

但是项目中H5的页面实在太多,很多公共的接口都是固定的,例如弹出用户登录,播放视频,打开图片游览器,还有跳转商品,活动,新闻,通知详情,所以就需要封装出一个框架,让所有的公共接口都可以相应公共接口的JS和原生互调,我这里采取的是用分类的方式,在这里采用分类的方式,设置代理,也有一些技巧:

1,在分类中定义一个代理,并用运行时实现setter和getter,这里设定代理的协议是UIWebViewDelegate的好处就是外面感觉不到代理转换了,还以为是直接的设置webview的代理,对外面暴露的越少,使用越简单。

@property(nonatomic,weak) IBInspectable id<UIWebViewDelegate>customDelegate;

-(id<UIWebViewDelegate>)customDelegate

{

return objc_getAssociatedObject(self, @selector(customDelegate));

}

-(void)setCustomDelegate:(id<UIWebViewDelegate>)customDelegate

{

objc_setAssociatedObject(self, @selector(customDelegate), customDelegate, OBJC_ASSOCIATION_ASSIGN);

}

2,设置自己的delegate是自己,并且实现代理方法,

self.delegate = self;

3,关键的地方来了,先上代码吧:

//让每个webView有依然可以设置代理并且相应各自的方法

-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType

{

//H5抛出的URL

NSString *absoluteString = request.URL.absoluteString;

BOOL action =  [self takeActionWith:absoluteString]; // 根据抛出的URL采取不同的操作

if (!action) {

return NO;

}

//执行每个webView自己的customDelegate代理方法

if (self.customDelegate && [self.customDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) {

return [self.customDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];

}

return YES;

}

拦截到的URL后根据标识字段采取各自的操作,并且判断是否需要继续加载当前URL,然后调用代理的加载方法,除了公共的接口外,还可以让外面的每个地方都自定义自己需要的操作

4,完工,给用户提供接口

-(void)setUrlString:(NSString *)urlString block:(void(^)(NSArray *arr))postImage delegate:(id<UIWebViewDelegate>)delegate;

然后在这个方法里面可以加载URL,然后设置代理,调用这个分类的地方,如果需要自定义UIWebViewDelegate的代理方法 ,也可以设置delegate,然后实现自己需要单独完成的操作,对外使用webview没有丝毫变化,加载url,设置代理,实现代理方法。

至此,封装出一个公共接口让JS和原生互调的分类就算完成了,在最开始我也是这么做的。但是随便版本迭代,交互越来越多,关键字段越来越多,通过字符串比对的方法,很容易跟其他的URL冲突,然后得到不想要的效果,而且,字符串比对还有先后顺序,如果一个URL包含两个关键字段,那么哪个字符串比对写在前面,就先执行哪个标识字段对应的函数。

显然,这是不合理的,也不是我们能容忍的

二,规定抛出URL的字段的规范

于是,我就想如何去解决这个问题,不过思前想后,还是觉得URL抛出没有任何规范,仅仅只是判断URL中是否包含某些字段的做法,其他地方的字段H5那边随便配备,显然是非常不合理的,于是,上网看了很多方案后,和后台约束的URL抛出字段的规范:

func://name=leehonn/age=24

这样的字段简洁明了,func是函数名,后面的都是参数,我们可以通过OC提供的API方法,先通过"//"将URL分割成一个数组,数组的第一个元素就是函数名,再把后面的字符串通过"/"分割,取得各自的参数,这样做的好处就是没有多余的字段,不容易冲突,需要的字段也都在,解析起来也方便。

当取得函数名,就可以执行函数了:

SEL originalSelector = NSSelectorFromString(absoluteString);

[self performSelector:originalSelector];

至此大功告成,优化完毕,而且这块和安卓可以通用,安卓那边也可以通过反射去执行函数,但是据说那样做性能会降低,但是即使不用反射,就用原来的字符串比对,也比之前URL的格式规范不少,不过对于iOS这边,确实是比之前有优化的。

不过后来我看微博,并不是采取的这样方案,他是协议头是一个固定字段,表示这个是要采取JS和原生交互的标识,然后主机地址就是函数名,后面接参数,感觉我上面那个更好一些。

三,采取第三方框架

后来还是感觉这种形式很蹩脚,就将眼光方向第三方框架,然后就找到了WebViewJavascriptBridge,星星数也挺多,然后就拿来用,另外,这个框架也是采取拦截URL的方式,只是他将细节封装起来了而已,而且这个框架也需要JS代码那边的配合,并且,这个框架也不像大家想的那样能够拦截JS的onClick事件,具体使用方法大家可以去github上看官方介绍,不过别忘了,这个框架是需要JS那边的代码配合的,需要JS为iOS专门适配一套,安卓那边也有自己的一套方案,如果你们做H5的同事不嫌麻烦,可以弄两套

还有据说可以通过stringByEvaluatingJavaScriptFromString函数就可以完成JS和原生的互调,有知道的大神希望分享下

转载于:https://www.cnblogs.com/leehonn/p/5644646.html

iOS下JS和原生交互,函数互调相关推荐

  1. iOS下JS与原生OC互相调用

    iOS开发免不了要与UIWebView打交道,然后就要涉及到JS与原生OC交互,今天总结一下JS与原生OC交互的两种方式. JS调用原生OC篇 方式一 第一种方式是用JS发起一个假的URL请求,然后利 ...

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

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

  3. iOS下JS与OC互相调用(五)--UIWebView + WebViewJavascriptBridge

    WebViewJavascriptBridge是一个有点年代的JS与OC交互的库,使用该库的著名应用还挺多的,目前这个库有7000+star.我去翻看了它的第一版本已经是4年前了,在版本V4.1.4以 ...

  4. iOS下JS与OC互相调用(六)--WKWebView + WebViewJavascriptBridge

    2019独角兽企业重金招聘Python工程师标准>>> iOS下JS与OC互相调用(六)--WKWebView + WebViewJavascriptBridge 转载:原地址 ht ...

  5. iOS下JS与OC互相调用(二)--WKWebView 拦截URL

    在上篇文章中讲述了使用UIWebView拦截URL的方式来处理JS与OC交互. 由于UIWebView比较耗内存,性能上不太好,而苹果在iOS 8中推出了WKWebView. 同样的用WKWebVie ...

  6. ios下js复制到粘贴板_h5实现一键复制到粘贴板 兼容ios

    实现原理 采用document.execCommand('copy') 来实现复制到粘贴板功能 复制必须是选中input框的文字内容,然后执行document.execCommand('copy') ...

  7. iOS下JS与OC互相调用(三)--MessageHandler

    使用WKWebView的时候,如果想要实现JS调用OC方法,除了拦截URL之外,还有一种简单的方式.那就是利用WKWebView的新特性MessageHandler来实现JS调用原生方法. Messa ...

  8. iOS下JS与OC互相调用(四)--JavaScriptCore

    前面讲完拦截URL的方式实现JS与OC互相调用,终于到JavaScriptCore了.它是从iOS7开始加入的,用 Objective-C 把 WebKit 的 JavaScript 引擎封装了一下, ...

  9. iOS下JS与OC互相调用(七)--Cordova 基础

    Cordova 简介 在介绍Cordova之前,必须先提一下PhoneGap.PhoneGap 是Nitobi软件公司2008年推出的一个框架,旨在弥补web 和iOS 之间的不足,使得web 和 i ...

最新文章

  1. 零基础小白学Java难度大不大
  2. 微信小程序界面跳转(2)——按钮
  3. 《TCP/IP详解》笔记----第四章 ARP协议
  4. js原生实现贪吃蛇(解决连续键盘事件)
  5. AcWing 840. 模拟散列表(散列hash)
  6. 设置UYVY格式YUV为黑色
  7. Android之WindowManager+OpenGL+EGL绘制(十七)
  8. 罗斯蒙特电磁流量计8723说明书_罗斯蒙特电磁流量计8732EM变送器信号处理算法说明...
  9. c语言的sin cos是弧度,C++中cos,sin,asin,acos這些三角函數操作的是弧度,而非角度(轉)...
  10. 从零开始做运营第一课:运营是做什么的?一篇文章解释清楚!
  11. a eraser eraser还是an_是a eraser还是an eraser
  12. Datawhale 202210 Excel | 第九、十章 Excel数据可视化
  13. 方法论:后台产品经理的前世今生(一)
  14. R mean() 函数 - 计算平均值
  15. android apk 安装目录成功,记录一次解决安装 Apk 兼容性问题
  16. win10 + Ubuntu 18.04.1 LTS (Bionic Beaver) 双系统的安装配置
  17. 如何在iPhone/iPad上录屏
  18. PyCharm 安装numpy包
  19. 【综合类型第 10 篇】什么是时间戳
  20. fatal error C1004: 发现意外的文件尾

热门文章

  1. php 会员到期提醒_会员管理系统花了50万都没做成,用这五个功能轻松实现
  2. mybatis多字段排序_解决mybatis中order by排序无效问题
  3. 不支持给定路径的格式_剑指offer_12_矩阵中的路径
  4. OpenCV-Python实战(14)——人脸检测详解(仅需6行代码学会4种人脸检测方法)
  5. c+命名空间_了解C ++中的命名空间
  6. seaborn_Seaborn Distplot:综合指南
  7. kotlin多继承_Kotlin继承
  8. android实例教程_活动之间的Android意向处理示例教程
  9. 7z.exe 命令参数
  10. Spark源码阅读——任务提交过程