iOS WKWebView与H5交互,JS调OC传值、OC调JS传值、进度条加载等(干货满满)
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS WKWebView与H5交互,JS调OC传值、OC调JS传值、进度条加载等(干货满满)相关的知识,希望对你有一定的参考价值。
参考技术A WKWebView是苹果在ios 8之后推出的框架,关于它比webview的优势这里就不讲了。主要说一下与JS交互的问题,其实WKWebView已经内置了JS与OC的互调、传值等方法,使用起来也非常方便,下面就来细细的探讨一下以及自己遇到过的坑...首先来看下WKWebView的初始化相关设置:
一、导入相关头文件、设置相关代理和属性
二、WKWebView初始化
注意:
楼主遇到的第一个坑:如果JS给OC传值为空,必须写成: postMessage(null),如果什么都不写,方法是调不通的。
1、在viewWillAppear中配置, addScriptMessageHandler name: "这里就是JS的方法,方法名必须统一"
楼主遇到的第二个坑:配置完后必须在 viewWillDisappear 中 remove,否则会造成循环引用,导致crash
2、实现 WKScriptMessageHandler 协议
以上就是JS调OC,JS向OC传值...
楼主这里举三个例子:
1: webview加载完成前,将用户信息传给js
2: webview加载完成,将相关信息传给js
3: 调用相册或相机时,将选择的图片请求后台接口,后台返回图片地址,将该地址回传给H5,H5将图片显示到页面上
第一个例子: webView加载完成前传值
因为 evaluatejavascript 方法默认是在加载完成后调用,所以直接在页面开始加载中调用是传不过去的,这个时候怎么办呢? 我们可以让js端写两个方法, 第一个方法是js端开始向oc端发起信息需求的方法名,当oc端收到该方法名的时候,就去调用js端第二个获取传值的方法,把信息传递过去。
先让JS端写个方法调OC,OC实现方法后在这个方法内部给JS传值
在WKScriptMessageHandler协议中,实现该方法,然后在方法内部给JS传值
注意: 以上就是在Webview加载完成前传值,如果打印没报错,证明传参成功,如果web端没收到,让他把获取到值的方法写到页面中即可。
第二个例子: webView加载完成,传值给js
第三个例子: 传图片地址给js,js拿到后显示图片
1:拍照事件
1.1:将拍的照片请求上传图片接口,成功返回图片地址,并传值给H5
2: 从相册中选取照片
2.2:将相册中选取的照片请求上传图片接口,成功返回图片地址,并传值给H5
注意: getPhotoCallback 即为调用的方法名,后面传值格式必须为: ('') , 这里遇到了第三个坑, 如果方法名写为: 名称.名称 (例如:hello. getPhotoCallback),这种是调不通的,可以写成hello_getPhotoCallback的形式,一般的话最好还是定义一个完整的名称。 刚开始这个问题卡了比较久,一直调不通,在此记录一下.....
在 viewDidLoad 中注册进度条监听
开始加载网页
加载完成
加载失败
页面跳转失败
progressView懒加载
添加监听观察者
最后别忘记 removeObserver
Demo地址: https://github.com/zhwIdea/WKWebViewAndJS
js与oc原生WKWebView交互传值
最近在做移动端实现H5支付,需要与JS交互,实现状态提醒,参数传值等,在这里总结一下,以防一个月后又忘了〜,
上一篇记录的app微信h5支付唤醒不了微信app,也是算一个bug有需要的可以去看一下
先看下四中交互方式:(我用的第三种,现在都是在用wkwebview不建议用webview)
1.拦截网址(适用于UIWebView和WKWebView)
2.JavaScriptCore(只适用于UIWebView,iOS7 +)
3.WKScriptMessageHandler(只适用于WKWebView,iOS8 +)
4.WebViewJavascriptBridge(适用于UIWebView和WKWebView,属于第三方框架)
1,2-不推荐
方法四WebViewJavascriptBridge
是一个第三方框架,官方文档和demo都很完整,不再累赘,GitHub地址:https:
//github.com/marcuswestin/WebViewJavascriptBridge
在这里主要记录一下方法三
@interface ViewController()<WKNavigationDelegate,UIScrollViewDelegate,WKUIDelegate,WKScriptMessageHandler>
@property(非原子,强)WKWebView * webView;
@结束
@implementation ViewController
- (void)viewDidLoad
[super viewDidLoad];
WKWebViewConfiguration * config = [[WKWebViewConfiguration alloc] init];
config.preferences = [[WKPreferences alloc] init];
config.preferences.minimumFontSize = 10;
config.preferences.javaScriptEnabled = YES;
config.preferences.javaScriptCanOpenWindowsAutomatically = NO;
config.userContentController = [[WKUserContentController alloc] init];
config.processPool = [[WKProcessPool alloc] init];
self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds
configuration:config];
//记得实现对应协议,不然方法不会实现.
self.webView.UIDelegate = self;
self.webView.navigationDelegate =self;
[self.view addSubview:self.webView];
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://192.168.1.188/index1.html"]]];
// **************** 此处划重点 **************** //
//添加注入js方法, oc与js端对应实现
[config.userContentController addScriptMessageHandler:self name:@"collectSendKey"];
[config.userContentController addScriptMessageHandler:self name:@"collectIsLogin"];
#pragma mark - WKScriptMessageHandler
//实现JS注入方法的协议方法
- (void)userContentController:(WKUserContentController *)userContentController
didReceiveScriptMessage:(WKScriptMessage *)message
//找到对应的js端的方法名,获取messge.body
if([message.name isEqualToString:@“collectSendKey”])
NSLog(@“%@”,message.body);
2.浏览网络页面,传递值给JS界面,JS界面通过值判断处理逻辑。
使用场景:浏览网页面商品,加入购物车,js通过oc原生传递过去的userId是否为空,来判断当前app是否登录,未登录,跳转原生界面登录,已登录,则直接加入购物车
直接放代码:
#pragma mark --------- WKNavigationDelegate --------------
//加载成功,传递值给js
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
//获取用户id
//传递userId给js端
NSString * userId = DEF_GET_OBJECT(UserID);
NSString * jsUserId;
if(!userId)
jsUserId = @“”;
其他
jsUserId = userId;
//之所以给userId重新赋值,貌似是如果userId为空null那么传给js端,js说无法判断,只好说,如果userId为null,重新定义为空字符串。如果大家有好的建议,可以在下方留言。
//同时,这个地方需要注意的是,JS端并不能查看我们给他传递的是什么值,也无法打印,貌似是语言问题?还是JS骗我文化低,反正,咱们把值传给他,根据双方商量好的逻辑,给出判断,如果正常,那就OK了。
NSString * jsStr = [NSString stringWithFormat:@“sendKey('%@')”,jsUserId];
[self.webView evaluateJavaScript:jsStr completionHandler:^(id _Nullable result,NSError * _Nullable error)
//此处可以打印错误。
];
// JS端获取传递值代码实现实例(此处为JS端实现代码给大家粘出来示范的!!!):
// function sendKey(user_id)
$( “#输入”)VAL(USER_ID)。
//依然是这个协议方法,获取注入方法名对象,获取JS返回的状态值。
#pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController
didReceiveScriptMessage:(WKScriptMessage *)message
// js端判断如果userId为空,则返回字符串@“toLogin”,或者返回其它值。JS端代码实现实例(此处为JS端实现代码给大家粘出来示范的!):
function collectIsLogin(goods_id)
if(/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent))
尝试
if($(“#input”)。val())
window.webkit.messageHandlers.collectGzhu.postMessage(body:“'”+ goods_id +“'”);
else
window.webkit.messageHandlers.collectGzhu.postMessage(body:'toLogin');
catch(e)
//浏览器
alert(e);
// OC原生处理:
if([message.name isEqualToString:@“collectIsLogin”])
NSDictionary * messageDict =(NSDictionary *)message.body;
if([messageDict [@“body”] isEqualToString:@“toLogin”])
的NSLog(@ “登录”);
其他
的NSLog(@ “正常跳转”);
NSLog(@“mess --- id ==%@”,message.body);
3.在交互中,关于alert(单对话框)函数,确认(是/否对话框)函数,提示(输入型对话框)函数时,实现代理协议WKUIDelegate,则系统方法里有三个对应的协议方法。大家可以进入WKUIDelegate协议类面面查看。下面具体协议方法实现,也给大家粘出来,以供参考。
#pragma mark - WKUIDelegate
- (void)webViewDidClose:(WKWebView *)webView
NSLog(@“%s”,__ FUNCTION__);
//在JS端调用alert函数时,会触发此代理方法。
// JS端调用alert时所传的数据可以通过消息拿到
//在原生得到结果后,需要回调JS,是通过completionHandler回调
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void(^)(void))completionHandler
NSLog(@“%s”,__ FUNCTION__);
UIAlertController * alert = [UIAlertController alertControllerWithTitle:@“alert”消息:@“JS调用警报”preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@“确定”样式:UIAlertActionStyleDefault处理程序:^(UIAlertAction * _Nnnull action)
completionHandler();
]];
[self presentViewController:alert animated:YES completion:NULL];
NSLog(@“%@”,消息);
// JS端调用确认函数时,会触发此方法
//通过消息可以拿到JS端所传的数据
//在iOS端显示原生警告得到是/否后
//通过completionHandler回调给JS端
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void(^)(BOOL result))completionHandler
NSLog(@“%s”,__ FUNCTION__);
UIAlertController * alert = [UIAlertController alertControllerWithTitle:@“confirm”消息:@“JS调用确认”preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@“确定”样式:UIAlertActionStyleDefault处理程序:^(UIAlertAction * _Nnnull action)
completionHandler(YES);
]];
[alert addAction:[UIAlertAction actionWithTitle:@“取消”样式:UIAlertActionStyleCancel处理程序:^(UIAlertAction * _Nnnull action)
completionHandler(NO);
]];
[self presentViewController:alert animated:YES completion:NULL];
NSLog(@“%@”,消息);
// JS端调用提示函数时,会触发此方法
//要求输入一段文本
//在原生输入得到文本内容后,通过completionHandler回调给JS
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText :( nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void(^)(NSString * __nullable result))completionHandler
NSLog(@“%s”,__ FUNCTION__);
NSLog(@“%@”,提示);
UIAlertController * alert = [UIAlertController alertControllerWithTitle:@“textinput”message:@“JS调用输入框”preferredStyle:UIAlertControllerStyleAlert];
[alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField)
textField.textColor = [UIColor redColor];
];
[alert addAction:[UIAlertAction actionWithTitle:@“确定”样式:UIAlertActionStyleDefault处理程序:^(UIAlertAction * _Nnnull action)
completionHandler([[alert.textFields lastObject] text]);
]];
[self presentViewController:alert animated:YES completion:NULL];
文章参考https://blog.csdn.net/dolacmeng/article/details/79623708
这篇文章也不错https://blog.csdn.net/hanhailong18/article/details/69102820
以上是关于iOS WKWebView与H5交互,JS调OC传值、OC调JS传值、进度条加载等(干货满满)的主要内容,如果未能解决你的问题,请参考以下文章