当前位置:   article > 正文

iOS WKWebView与JS交互、传值,H5调相机相册、进度条加载等(干货满满)_ioswkwebview调用拍照

ioswkwebview调用拍照

WKWebView是苹果在iOS 8之后推出的框架,关于它比webview的优势这里就不讲了。主要说一下与JS交互的问题,其实WKWebView已经内置了JS与OC的互调、传值等方法,使用起来也非常方便,下面就来细细的探讨一下以及自己遇到过的坑...

首先来看下WKWebView的初始化相关设置:

一、导入相关头文件、设置相关代理和属性

调用相册楼主用的是: TZImagePickerController框架, 如果你用的系统或其他的,直接替换就行

  1. #import "WebViewController.h"
  2. #import "webkit/webkit.h"
  3. #define WS(weakSelf) __weak __typeof(&*self) weakSelf = self
  4. #define kWidth [UIScreen mainScreen].bounds.size.width
  5. #define kHeight [UIScreen mainScreen].bounds.size.height
  6. @interface WebViewController ()<WKNavigationDelegate,WKScriptMessageHandler,WKUIDelegate,TZImagePickerControllerDelegate,UIImagePickerControllerDelegate, UINavigationControllerDelegate>
  7. @property(nonatomic,strong)WKWebView *webView;
  8. @property (nonatomic, strong) UIProgressView *progressView; //进度条加载
  9. @property (nonatomic, strong) NSMutableArray *imgArray;// 图片数组
  10. @property (nonatomic,strong)WKWebViewConfiguration *configuration;
  11. @end

二、WKWebView初始化

  1. - (void)viewDidLoad {
  2. [super viewDidLoad];
  3. // 配置网页的配置文件
  4. WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
  5. WKPreferences *preference = [[WKPreferences alloc]init];
  6. configuration.preferences = preference;
  7. configuration.selectionGranularity = YES; //允许与网页交互
  8. // webView初始化
  9. self.webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, kWidth, kHeight) configuration:configuration];
  10. self.view.backgroundColor = [UIColor whiteColor];
  11. self.webView.UIDelegate = self;
  12. self.webView.navigationDelegate = self;
  13. _webView.allowsBackForwardNavigationGestures = YES; //二级网页是否可以左划返回
  14. // 楼主这里隐藏了原生导航栏,加载的 H5导航栏,下拉不允许导航栏跟着下拉,设置弹簧效果为NO即可
  15. _webView.scrollView.bounces = NO;
  16. [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:self.urlStr]]];
  17. [self.view addSubview:self.webView];
  18. }

接下来才是重点!!!

一、JS调OC,JS给OC传值

  1. // JS调OC,需要 H5端统一如下写法,方法名就是交互的名称,数据就是JS给OC传的值
  2. window.webkit.messageHandlers.<方法名>.postMessage(<数据>)

注意:
楼主遇到的第一个坑:如果JS给OC传值为空,必须写成: postMessage(null),如果什么都不写,方法是调不通的。

1、在viewWillAppear中配置, addScriptMessageHandler name: "这里就是JS的方法,方法名必须统一"

  1. -(void)viewWillAppear:(BOOL)animated{
  2. [super viewWillAppear:animated];
  3. #pragma mark ======= JS事件 ========
  4. //因为楼主的导航栏是隐藏了的,显示的是H5的导航栏,所以要调用返回按钮到主页面
  5. [self.webView.configuration.userContentController addScriptMessageHandler:self name:@"back"];
  6. //拍照
  7. [self.webView.configuration.userContentController addScriptMessageHandler:self name:@"camera"];
  8. //从相册选取
  9. [self.webView.configuration.userContentController addScriptMessageHandler:self name:@"album"];
  10. //H5请求接口时,调用原生的指示器加载
  11. [self.webView.configuration.userContentController addScriptMessageHandler:self name:@"loadIndicator"];
  12. //接口请求完成,隐藏指示器
  13. [self.webView.configuration.userContentController addScriptMessageHandler:self name:@"hiddenIndicator"];
  14. }

楼主遇到的第二个坑:配置完后必须在viewWillDisappear中 remove,否则会造成循环引用,导致crash

  1. - (void)viewWillDisappear:(BOOL)animated
  2. {
  3. [super viewWillDisappear:animated];
  4. // 这里要记得移除handlers
  5. [self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"back"];
  6. [self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"camera"];
  7. [self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"album"];
  8. [self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"loadIndicator"];
  9. [self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"hiddenIndicator"];
  10. }

2、实现 WKScriptMessageHandler 协议

  1. //WKScriptMessageHandler协议方法
  2. - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
  3. // message.body 即为JS向OC传的值
  4. id body = message.body;
  5. NSLog(@"=== %@", body);
  6. if ([message.name isEqualToString:@"back"]) {
  7. //返回到首页
  8. [self.navigationController popViewControllerAnimated:YES];
  9. }if ([message.name isEqualToString:@"camera"]) {
  10. //拍照
  11. [self takePhotos];
  12. } if ([message.name isEqualToString:@"album"]) {
  13. //从相册选取
  14. [self localPhotos];
  15. }if ([message.name isEqualToString:@"loadIndicator"]) {
  16. //加载指示器
  17. [SVProgressHUD show];
  18. }if ([message.name isEqualToString:@"hiddenIndicator"]) {
  19. //隐藏指示器
  20. [SVProgressHUD dismiss];
  21. }
  22. }

以上就是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传值

  1. -(void)viewWillAppear:(BOOL)animated{
  2. [super viewWillAppear:animated];
  3. //发起信息需求
  4. [self.webView.configuration.userContentController addScriptMessageHandler:self name:@"getUserInfo"];

在WKScriptMessageHandler协议中,实现该方法,然后在方法内部给JS传值

  1. - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
  2. id body = message.body;
  3. NSLog(@"=== %@", body);
  4. if ([message.name isEqualToString:@"getUserInfo"]) {
  5. NSLog(@"getUserInfo");
  6. //在这里给JS传值
  7. NSDictionary *dict = @{@"id":@"123", @"name":@"lisi"};
  8. //转为json
  9. NSData *data = [NSJSONSerialization dataWithJSONObject:dict options:(NSJSONWritingPrettyPrinted) error:nil];
  10. NSString *jsonStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
  11. NSLog(@"jsonStr == %@",jsonStr);
  12. //给js传值,获取用户信息
  13. NSString *inputValueJS = [NSString stringWithFormat:@"getCurrentUser('%@')", jsonStr];
  14. [webView evaluateJavaScript:inputValueJS completionHandler:^(id _Nullable response, NSError * _Nullable error) {
  15. //打印如果errornull,表示已调通
  16. NSLog(@"value: %@ error: %@", response, error);
  17. }];
  18. }
  19. }

注意:以上就是在Webview加载完成前传值,如果打印没报错,证明传参成功,如果web端没收到,让他把获取到值的方法写到页面中即可。

第二个例子: webView加载完成,传值给js

  1. - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
  2. NSDictionary *dict = @{@"id":@"123", @"name":@"lisi"};
  3. //转为json
  4. NSData *data = [NSJSONSerialization dataWithJSONObject:dict options:(NSJSONWritingPrettyPrinted) error:nil];
  5. NSString *jsonStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
  6. NSLog(@"jsonStr == %@",jsonStr);
  7. //给js传值,获取用户信息
  8. NSString *inputValueJS = [NSString stringWithFormat:@"getCurrentUser('%@')", jsonStr];
  9. [webView evaluateJavaScript:inputValueJS completionHandler:^(id _Nullable response, NSError * _Nullable error) {
  10. //打印如果error都为null,表示已调通
  11. NSLog(@"value: %@ error: %@", response, error);
  12. }];
  13. }

第三个例子: 传图片地址给js,js拿到后显示图片

1:拍照事件

  1. //拍照
  2. - (void)takePhotos{
  3. UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypeCamera;
  4. if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
  5. UIImagePickerController *picker = [[UIImagePickerController alloc]init];
  6. picker.delegate = self;
  7. picker.allowsEditing = YES;
  8. picker.sourceType = sourceType;
  9. [self.navigationController presentViewController:picker animated:YES completion:^{
  10. NSLog(@"OK");
  11. }];
  12. }
  13. else {
  14. NSLog(@"模拟其中无法打开照相机,请在真机中使用");
  15. }
  16. }

1.1:将拍的照片请求上传图片接口,成功返回图片地址,并传值给H5

  1. // 相机
  2. - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
  3. [picker dismissViewControllerAnimated:YES completion:^{}];
  4. [self.imgArray removeAllObjects];
  5. UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
  6. [self.imgArray addObject:image];
  7. if (self.imgArray.count) {
  8. //这里开始写请求上传图片接口的代码
  9. //请求成功,获取返回的图片地址,如果是数组,将数组转换为字符串
  10. NSString *urlStr = [[数组] componentsJoinedByString:@""];
  11. // 然后向js传图片地址:
  12. NSString *inputValue = [NSString stringWithFormat:@"getPhotoCallback('%@')",urlStr];
  13. [self.webView evaluateJavaScript:inputValue completionHandler:^(id _Nullable response, NSError * _Nullable error) {
  14. NSLog(@"value图片: %@ error: %@", response, error);
  15. }];
  16. }
  17. }

2: 从相册中选取照片

  1. #pragma mark TZImagePickerControllerDelegate
  2. #pragma mark -- 从相册中选择照片
  3. -(void)localPhotos{
  4. TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:1 delegate:self];
  5. [self.navigationController presentViewController:imagePickerVc animated:YES completion:nil];
  6. }
  7. /// 用户点击了取消
  8. - (void)imagePickerControllerDidCancel:(TZImagePickerController *)picker {
  9. [self dismissViewControllerAnimated:YES completion:nil];
  10. }

2.2:将相册中选取的照片请求上传图片接口,成功返回图片地址,并传值给H5

  1. // 相册
  2. /// 用户选择好了图片,如果assets非空,则用户选择了原图。
  3. - (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingPhotos:(NSArray *)photos sourceAssets:(NSArray *)assets{
  4. [self.imgArray removeAllObjects];
  5. for (int i = 0; i < photos.count; i++) {
  6. UIImage *image = photos[i];
  7. [self.imgArray addObject:image];
  8. }
  9. if (self.imgArray.count) {
  10. //这里开始写请求上传图片接口的代码
  11. //请求成功,获取返回的图片地址,如果是数组,将数组转换为字符串
  12. NSString *urlStr = [[数组] componentsJoinedByString:@""];
  13. // 然后向js传图片地址:
  14. NSString *inputValue = [NSString stringWithFormat:@"getPhotoCallback('%@')",urlStr];
  15. [self.webView evaluateJavaScript:inputValue completionHandler:^(id _Nullable response, NSError * _Nullable error) {
  16. NSLog(@"value图片: %@ error: %@", response, error);
  17. }];
  18. }
  19. }

注意: getPhotoCallback即为调用的方法名,后面传值格式必须为:(''), 这里遇到了第三个坑,如果方法名写为: 名称.名称 (例如:hello. getPhotoCallback),这种是调不通的,可以写成hello_getPhotoCallback的形式,一般的话最好还是定义一个完整的名称。刚开始这个问题卡了比较久,一直调不通,在此记录一下.....

以上就是OC调JS,OC给JS传值的分享!!!

最后分享一下:

WKWebView进度条加载....

viewDidLoad 中注册进度条监听

  1. - (void)viewDidLoad {
  2. [super viewDidLoad];
  3. //添加进度条监听
  4. [self.webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
  5. }

开始加载网页

  1. - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation
  2. {
  3. //显示
  4. self.progressView.hidden = NO;
  5. self.progressView.transform = CGAffineTransformMakeScale(1.0f, 1.5f);
  6. [self.view bringSubviewToFront:self.progressView];
  7. }

加载完成

  1. - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
  2. //隐藏
  3. self.progressView.hidden = YES;
  4. }

加载失败

  1. // 页面加载失败时调用
  2. - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error
  3. {
  4. self.progressView.hidden = YES;
  5. }

页面跳转失败

  1. //页面跳转失败时调用
  2. - (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error
  3. {
  4. if(error.code==NSURLErrorCancelled)
  5. {
  6. [self webView:webView didFinishNavigation:navigation];
  7. }else{
  8. self.progressView.hidden = YES;
  9. }
  10. }

progressView懒加载

  1. - (UIProgressView *)progressView
  2. {
  3. if (!_progressView)
  4. {
  5. _progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(0, Height_StatusBar, kWidth, 2)];
  6. _progressView.backgroundColor = [UIColor blueColor];
  7. _progressView.transform = CGAffineTransformMakeScale(1.0f, 1.5f);
  8. _progressView.progressTintColor = [UIColor blueColor];
  9. [self.view addSubview:self.progressView];
  10. }
  11. return _progressView;
  12. }

添加监听观察者

  1. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
  2. {
  3. if ([keyPath isEqualToString:@"estimatedProgress"])
  4. {
  5. self.progressView.progress = self.webView.estimatedProgress;
  6. if (self.progressView.progress == 1)
  7. {
  8. WS(weakSelf);
  9. [UIView animateWithDuration:0.25f delay:0.3f options:UIViewAnimationOptionCurveEaseOut animations:^
  10. {
  11. weakSelf.progressView.transform = CGAffineTransformMakeScale(1.0f, 1.4f);
  12. }
  13. completion:^(BOOL finished)
  14. {
  15. weakSelf.progressView.hidden = YES;
  16. }];
  17. }
  18. }
  19. }

最后别忘记 removeObserver

  1. -(void)dealloc{
  2. [self.webView removeObserver:self forKeyPath:@"estimatedProgress"];
  3. }

结语:

以上就是有关 WKWebView与JS交互、进度条加载的分享

如有问题请下方留言指正!

如有帮助请?支持一下 ?

Demo地址: https://github.com/zhwIdea/WKWebViewAndJS

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/175104?site
推荐阅读
相关标签
  

闽ICP备14008679号