iOSWKWebView与JS的交互
WKWebView是苹果在iOS 8之后推出的框架,关于它⽐webview的优势这⾥就不讲了。主要说⼀下与JS交互的问题,其实WKWebView已经内置了JS与OC的互调、传值等⽅法,使⽤起来也⾮常⽅便,下⾯就来细细的探讨⼀下以及⾃⼰遇到过的坑...
⼀、导⼊相关头⽂件、设置相关代理和属性
调⽤相册楼主⽤的是: , 如果你⽤的系统或其他的,直接替换就⾏
#import"WebViewController.h"
#import"webkit/webkit.h"
#define WS(weakSelf) __weak __typeof(&*self) weakSelf = self
#define kWidth [UIScreen mainScreen].bounds.size.width
#define kHeight [UIScreen mainScreen].bounds.size.height
@interface WebViewController ()<WKNavigationDelegate,WKScriptMessageHandler,WKUIDelegate,T
ZImagePickerControllerDelegate,UIImagePickerControllerDelegate, UINavigationControllerDelegate> @property(nonatomic,strong)WKWebView *webView;
@property (nonatomic, strong) UIProgressView *progressView; //进度条加载
@property (nonatomic, strong) NSMutableArray *imgArray;// 图⽚数组
@property (nonatomic,strong)WKWebViewConfiguration *configuration;
@end
⼆、WKWebView初始化
- (void)viewDidLoad {
[super viewDidLoad];
// 配置⽹页的配置⽂件
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
WKPreferences *preference = [[WKPreferences alloc]init];
configuration.preferences = preference;
configuration.selectionGranularity = YES; //允许与⽹页交互
// webView初始化
self.webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, kWidth, kHeight) configuration:configuration];
self.view.backgroundColor = [UIColor whiteColor];
self.webView.UIDelegate = self;
self.webView.navigationDelegate = self;
_webView.allowsBackForwardNavigationGestures = YES; //⼆级⽹页是否可以左划返回
// 楼主这⾥隐藏了原⽣导航栏,加载的 H5导航栏,下拉不允许导航栏跟着下拉,设置弹簧效果为NO即可
_webView.scrollView.bounces = NO;
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.urlStr]]];
[self.view addSubview:self.webView];
}
接下来才是重点
⼀、JS调OC,JS给OC传值
// JS调OC,需要 H5端统⼀如下写法,⽅法名就是交互的名称,数据就是JS给OC传的值
ssageHandlers.<⽅法名>.postMessage(<;数据>)
注意:
楼主遇到的第⼀个坑:如果JS给OC传值为空,必须写成: postMessage(null),如果什么都不写,⽅法是调不通的。
1、在viewWillAppear中配置,addScriptMessageHandler name: "这⾥就是JS的⽅法,⽅法名必须统⼀"
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
#pragma mark ======= JS事件 ========
//因为楼主的导航栏是隐藏了的,显⽰的是H5的导航栏,所以要调⽤返回按钮到主页⾯
[figuration.userContentController addScriptMessageHandler:self name:@"back"];
//拍照
[figuration.userContentController addScriptMessageHandler:self name:@"camera"];
//从相册选取
[figuration.userContentController addScriptMessageHandler:self name:@"album"];
//H5请求接⼝时,调⽤原⽣的指⽰器加载
[figuration.userContentController addScriptMessageHandler:self name:@"loadIndicator"];
//接⼝请求完成,隐藏指⽰器
[figuration.userContentController addScriptMessageHandler:self name:@"hiddenIndicator"];
}
楼主遇到的第⼆个坑:配置完后必须在viewWillDisappear中 remove,否则会造成循环引⽤,导致crash
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
// 这⾥要记得移除handlers
[figuration.userContentController removeScriptMessageHandlerForName:@"back"];
[figuration.userContentController removeScriptMessageHandlerForName:@"camer
a"];
[figuration.userContentController removeScriptMessageHandlerForName:@"album"];
[figuration.userContentController removeScriptMessageHandlerForName:@"loadIndicator"];
[figuration.userContentController removeScriptMessageHandlerForName:@"hiddenIndicator"];
}
2、实现 WKScriptMessageHandler 协议
//WKScriptMessageHandler协议⽅法
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
// message.body 即为JS向OC传的值
id body = message.body;
NSLog(@"=== %@", body);
if ([message.name isEqualToString:@"back"]) {
//返回到⾸页
[self.navigationController popViewControllerAnimated:YES];
}if ([message.name isEqualToString:@"camera"]) {
//拍照
[self takePhotos];
} if ([message.name isEqualToString:@"album"]) {
//从相册选取
[self localPhotos];
}if ([message.name isEqualToString:@"loadIndicator"]) {
//加载指⽰器
[SVProgressHUD show];
}if ([message.name isEqualToString:@"hiddenIndicator"]) {
//隐藏指⽰器
[SVProgressHUD dismiss];
}
}
以上就是JS调OC,JS向OC传值...
⼆、OC调JS,OC向JS传值
实现该⽅法即可:
[webView evaluateJavaScript:<> completionHandler:^(id _Nullable response, NSError * _Nullable error){}];
楼主这⾥举三个例⼦:
1: webview加载完成前,将⽤户信息传给js
2: webview加载完成,将相关信息传给js
3: 调⽤相册或相机时,将选择的图⽚请求后台接⼝,后台返回图⽚地址,将该地址回传给H5,H5将图⽚显⽰到页⾯上
第⼀个例⼦: webView加载完成前传值
因为 evaluateJavaScript ⽅法默认是在加载完成后调⽤,所以直接在页⾯开始加载中调⽤是传不过去的,这个时候怎么办呢?我们可以让js端写两个⽅法,第⼀个⽅法是js端开始向oc端发起信息需求的⽅法名,当oc端收到该⽅法名的时候,就去调⽤js端第⼆个获取传值的⽅法,把信息传递过去。
先让JS端写个⽅法调OC,OC实现⽅法后在这个⽅法内部给JS传值
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
//发起信息需求
[figuration.userContentController addScriptMessageHandler:self name:@"getUserInfo"];
}
在WKScriptMessageHandler协议中,实现该⽅法,然后在⽅法内部给JS传值
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
id body = message.body;
NSLog(@"=== %@", body);
if ([message.name isEqualToString:@"getUserInfo"]) {
NSLog(@"getUserInfo");
//在这⾥给JS传值
NSDictionary *dict = @{@"id":@"123", @"name":@"lisi"};
//转为json
NSData *data = [NSJSONSerialization dataWithJSONObject:dict options:(NSJSONWritingPrettyPrinted) error:nil];
NSString *jsonStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"jsonStr == %@",jsonStr);
//给js传值,获取⽤户信息
NSString *inputValueJS = [NSString stringWithFormat:@"getCurrentUser('%@')", jsonStr];
[webView evaluateJavaScript:inputValueJS completionHandler:^(id _Nullable response, NSError * _Nullable error) {
//打印如果error为null,表⽰已调通
NSLog(@"value: %@ error: %@", response, error);
}];
}
}
注意:以上就是在Webview加载完成前传值,如果打印没报错,证明传参成功,如果web端没收到,让他把获取到值的⽅法写到页⾯中即可。
第⼆个例⼦: webView加载完成,传值给js
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
NSDictionary *dict = @{@"id":@"123", @"name":@"lisi"};
//转为json
NSData *data = [NSJSONSerialization dataWithJSONObject:dict options:(NSJSONWritingPrettyPrinted) error:nil];
NSString *jsonStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"jsonStr == %@",jsonStr);
//给js传值,获取⽤户信息
NSString *inputValueJS = [NSString stringWithFormat:@"getCurrentUser('%@')", jsonStr];
[webView evaluateJavaScript:inputValueJS completionHandler:^(id _Nullable response, NSError * _Nullable error) {
//打印如果error都为null,表⽰已调通
NSLog(@"value: %@ error: %@", response, error);
}];
}
第三个例⼦: 传图⽚地址给js,js拿到后显⽰图⽚
1:拍照事件
//拍照
- (void)takePhotos{
UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypeCamera;
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
UIImagePickerController *picker = [[UIImagePickerController alloc]init];
picker.delegate = self;
picker.allowsEditing = YES;
picker.sourceType = sourceType;
[self.navigationController presentViewController:picker animated:YES completion:^{
NSLog(@"OK");
}];
}
else {
NSLog(@"模拟其中⽆法打开照相机,请在真机中使⽤");
}
}
1.1:将拍的照⽚请求上传图⽚接⼝,成功返回图⽚地址,并传值给H5
// 相机
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
[picker dismissViewControllerAnimated:YES completion:^{}];
[self.imgArray removeAllObjects];
UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
[self.imgArray addObject:image];
if (unt) {
//这⾥开始写请求上传图⽚接⼝的代码
//请求成功,获取返回的图⽚地址,如果是数组,将数组转换为字符串
NSString *urlStr = [[数组] componentsJoinedByString:@""];
// 然后向js传图⽚地址:
NSString *inputValue = [NSString stringWithFormat:@"getPhotoCallback('%@')",urlStr];
[self.webView evaluateJavaScript:inputValue completionHandler:^(id _Nullable response, NSError * _Nullable error) {
NSLog(@"value图⽚: %@ error: %@", response, error);
}];
}
}
2: 从相册中选取照⽚
#pragma mark TZImagePickerControllerDelegate
#pragma mark -- 从相册中选择照⽚
-(void)localPhotos{
TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:1delegate:self];
[self.navigationController presentViewController:imagePickerVc animated:YES completion:nil];
}
///⽤户点击了取消
- (void)imagePickerControllerDidCancel:(TZImagePickerController *)picker {
[self dismissViewControllerAnimated:YES completion:nil];
}
2.2:将相册中选取的照⽚请求上传图⽚接⼝,成功返回图⽚地址,并传值给H5
// 相册
///⽤户选择好了图⽚,如果assets⾮空,则⽤户选择了原图。
- (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingPhotos:(NSArray *)photos sourceAssets:(NSArray *)assets{
[self.imgArray removeAllObjects];
for (int i = 0; i < unt; i++) {
UIImage *image = photos[i];
[self.imgArray addObject:image];
}
if (unt) {
js获取json的key和value//这⾥开始写请求上传图⽚接⼝的代码
//请求成功,获取返回的图⽚地址,如果是数组,将数组转换为字符串
NSString *urlStr = [[数组] componentsJoinedByString:@""];
// 然后向js传图⽚地址:
NSString *inputValue = [NSString stringWithFormat:@"getPhotoCallback('%@')",urlStr];
[self.webView evaluateJavaScript:inputValue completionHandler:^(id _Nullable response, NSError * _Nullable error) {
NSLog(@"value图⽚: %@ error: %@", response, error);
}];
}
}
注意: getPhotoCallback即为调⽤的⽅法名,后⾯传值格式必须为:(''),这⾥遇到了第三个坑,如果⽅法名写为: 名称.名称 (例如:hello. getPhotoCallback),这种是调不通的,可以写成hello_getPhotoCallback的形式,⼀般的话最好还是定义⼀个完整的名称。刚开始这个问题卡了⽐较久,⼀直调不通,在此记录⼀下.....
以上就是OC调JS,OC给JS传值的分享
最后分享⼀下:
WKWebView进度条加载....
在 viewDidLoad 中注册进度条监听
- (void)viewDidLoad {
[super viewDidLoad];
//添加进度条监听
[self.webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
}
开始加载⽹页
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation
{
//显⽰
self.progressView.hidden = NO;
ansform = CGAffineTransformMakeScale(1.0f, 1.5f);
[self.view bringSubviewToFront:self.progressView];
}
加载完成
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
/
/隐藏
self.progressView.hidden = YES;
}
加载失败
// 页⾯加载失败时调⽤
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error
{
self.progressView.hidden = YES;
}
页⾯跳转失败
//页⾯跳转失败时调⽤
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error
{
de==NSURLErrorCancelled)
{
[self webView:webView didFinishNavigation:navigation];
}else{
self.progressView.hidden = YES;
}
}
progressView懒加载
- (UIProgressView *)progressView
{
if (!_progressView)
{
_progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(0, Height_StatusBar, kWidth, 2)];
_progressView.backgroundColor = [UIColor blueColor];
_ansform = CGAffineTransformMakeScale(1.0f, 1.5f);
_progressView.progressTintColor = [UIColor blueColor];
[self.view addSubview:self.progressView];
}
return _progressView;
}
添加监听观察者
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if ([keyPath isEqualToString:@"estimatedProgress"])
{
self.progressView.progress = self.webView.estimatedProgress;
if (self.progressView.progress == 1)
{
WS(weakSelf);
[UIView animateWithDuration:0.25f delay:0.3f options:UIViewAnimationOptionCurveEaseOut animations:^
{
ansform = CGAffineTransformMakeScale(1.0f, 1.4f);
}
completion:^(BOOL finished)
{
weakSelf.progressView.hidden = YES;
}];
}
}
}
最后别忘记 removeObserver
-(void)dealloc{
[self.webView removeObserver:self forKeyPath:@"estimatedProgress"];
}
结语:
以上就是有关 WKWebView与JS交互、进度条加载的分享
如有问题请下⽅留⾔指正!
如有帮助请?⽀持⼀下 ?
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论