威立山

记录心路历程

0%

OC和JavaScript交互

UIWebView与JavaScript交互

基本的概念

  • JavaScriptCore是封装了JavaScript和Objective-C桥接的Objective-C API,只要用很少的代码,就可以做到JavaScript调用Objective-C,或者Objective-C调用JavaScript。
  • JSValue: 代表一个JavaScript实体,一个JSValue可以表示很多JavaScript原始类型例如boolean, integers, doubles,甚至包括对象和函数。
  • JSContext: 代表JavaScript的运行环境,你需要用JSContext来执行JavaScript代码。所有的JSValue都是捆绑在一个JSContext上的.
  • JSExport: 这是一个协议,可以用这个协议来将原生对象导出给JavaScript,这样原生对象的属性或方法就成为了JavaScript的属性或方法,非常神奇。

OC调用JS的方法

采用stringByEvaluatingJavaScriptFromString写入JS代码,调用JS的方法
采用系统框架<JavaScriptCore/JavaScriptCore.h>的JSContext +evaluateScript调用JS代码

1
2
3
4
5
6
// 1.UIWebView执行JS代码
[self.webView stringByEvaluatingJavaScriptFromString:@"showName('Willi')"];
// 2.JavaScriptCore执行JS
JSContext *context = [[JSContext alloc] init];
NSString *jsCode = [NSString stringWithFormat:@"alert(\"我是OC里面的js方法\")"];
[context evaluateScript:jsCode];

JS调用OC的方法

  1. 采用传统的方法,加载WebView的时候截取URL的方式
  2. 利用苹果系统框架<JavaScriptCore/JavaScriptCore.h>的JSContext的block方式 或者 JSExport协议方式
1
2
3
4
5
6
7
8
9
//注册printAandB方法
context[@"printAandB"] = ^(NSString *A ,NSString *B) {
NSLog(@"%@,%@",A,B);
};

//自定义协议,并且协议遵守<JSExport>协议
@protocol WuKongJSExport <JSExport>
JSExportAs(Invoke, - (void)invokeKey:(NSString *)key value:(NSString *)value);
@end

WKWebView与JavaScript的交互

WKWebView

WKWebView是苹果在iOS 8中引入的新组件,目的是给出一个新的高性能的WebView解决方案,摆脱过去 UIWebView的老、旧、笨重,特别是内存占用量巨大的问题。

OC调用JS的方法

  • 原生调用JavaScript的代码需要在页面加载完成之后,就是在 - webView:didFinishNavigation:代理方法里面
1
2
3
[webView evaluateJavaScript:@"showAlert('奏是一个弹框')" completionHandler:^(id item, NSError * _Nullable error) {
// Block中处理是否通过了或者执行JS错误的代码
}];

JS调用OC的方法

  • JavaScript的配置
    JavaScript调用Native的方法就需要前端和Native的小伙伴们配合了,需要前端的小伙伴在JS的方法中调用:
    window.webkit.messageHandlers.NativeMethod.postMessage("就是一个消息啊");

  • Native App的代码配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 // 创建配置
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
// 创建UserContentController(提供JavaScript向webView发送消息的方法)
WKUserContentController* userContent = [[WKUserContentController alloc] init];
// 添加消息处理,注意:self指代的对象需要遵守WKScriptMessageHandler协议,结束时需要移除
[userContent addScriptMessageHandler:self name:@"NativeMethod"];
// 将UserConttentController设置到配置文件
config.userContentController = userContent;
// 高端的自定义配置创建WKWebView
WKWebView *webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds configuration:config];
// 设置访问的URL
NSURL *url = [NSURL URLWithString:@"http://www.jianshu.com"];
// 根据URL创建请求
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// WKWebView加载请求
[webView loadRequest:request];
// 将WKWebView添加到视图
[self.view addSubview:webView];

可以看到,添加消息处理的handler的name,就是JavaScript中调用时候的NativeMethod,这两个要保持一致。请把URL换成你自己的。
配置当前ViewController为MessageHandler,需要服从WKScriptMessageHandler协议,如果出现警告⚠️,请检查是否服从了这个协议。
注意!注意!注意:上面将当前ViewController设置为MessageHandler之后需要在当前ViewController销毁前将其移除,否则会造成内存泄漏。

1
[webView.configuration.userContentController removeScriptMessageHandlerForName:@"NativeMethod"];

现在万事俱备,只欠东风了。可以看到WKScriptMessageHandler的协议里面只有一个方法,就是:- userContentController:didReceiveScriptMessage:

相信聪明的你已经猜到了。是的,就是在这个代理方法里面操作:如果JavaScript执行已经写好的:window.webkit.messageHandlers.NativeMethod.postMessage(“就是一个消息啊”);这行代码,这个代理方法就会走,并且会有个WKScriptMessage的对象,这个WKScriptMessage对象有个name属性,拿到之后你会发现,就是我们注册的NativeMethod这个字符串,这时候你就可以手动调用Native的方法了。如果有多个方法需要调用的话怎么办,看到JavaScript中postMessage()方法有一个参数了没有,可以根据这里的参数来区分调用原生App的哪个方法。

1
2
3
4
5
6
7
8
9
10
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
// 判断是否是调用原生的
if ([@"NativeMethod" isEqualToString:message.name]) {
// 判断message的内容,然后做相应的操作
if ([@"close" isEqualToString:message.body]) {

}
}
}

网页支持滑动返回

WKWebView比UIWebView有很多优势,在WKWebView中通过设置一个属性 allowsBackForwardNavigationGestures 就可以实现滑动返回,而UIWebView需要写很多代码来达到滑动返回效果。

项目最低支持iOS7,所以只能使用UIKit中的UIWebView,而新的WKWebView是支持iOS8+

  • WKWebView的内存开销要比UIWebView小很多
  • 拥有高达60FPS滚动刷新率及内置手势
  • 支持了更多的HTML5特性
  • html页面和WKWebView交互更方便
  • Safari相同的JavaScript引擎
  • 提供常用的属性,如加载网页进度的属性estimatedProgress

实现思路:在UIWebView的Delegate的shouldStart代理方法中,把当前页面截一张图,把这张截图和对应的Url保存到一个数组中,在WebView上加一个UIPanGestureRecognizer拖拽手势,在手势代理方法中监听状态的变化,当手指滑动时,会把当前的截图和上一页截图,根据手势的移动的位置,做随手指移动的动画。然后重新请求上一个网页
测试过程中发现,有时滑动好使有时不好使,最后了解到这个Pan手势和UIScrollview的Pan手势会冲突

  1. [webpageView.scrollView.panGestureRecognizer requireGestureRecognizerToFail:_swipePanGesture]; 设置先识别滑动返回,在识别滚动的手势,偶尔会造成某些网站网页,不能滚动。

  2. 设置手势代理方法 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer 返回YES,允许手势能同时识别。完美解决手势冲突


欢迎指正😝

资料

WKWebView使用
WKWeb​View(AFN作者)
自己动手打造基于 WKWebView 的混合开发框架收藏一下