美文网首页HTML交互iOS Coding编程
iOS OC与JS交互+调用系统相册和相机并把图片传至页面+we

iOS OC与JS交互+调用系统相册和相机并把图片传至页面+we

作者: 三点水老木头 | 来源:发表于2017-02-23 17:46 被阅读2160次

    先说下这篇文章的预期目标,就是利用原生库JavaScriptCore怎么在实际应用中进行交互,例子展示怎么在前端页面调用OC代码调出系统相册和相机,OC怎么把图片传至前端页面,顺便简单写写怎么把进度条封装到webView里面
    这里使用组合来写这个例子,跟着一步步来
    新建一个继承于UIView的类ZSZWebView,然后加上webView和progressView,代码如下

    //  ZSZWebVIew.h
    @interface ZSZWebVIew : UIView
    
    @end
    
    #import "ZSZWebVIew.h"
    @interface ZSZWebVIew()<UIWebViewDelegate>
    
    @property (nonatomic, strong) UIWebView *myWebView;
    @property (nonatomic, strong) UIProgressView *progressView;
    
    @end
    
    

    暂时声明两个初始化方法方便初始化这个类

    //  ZSZWebVIew.h
    @interface ZSZWebVIew : UIView
    -(instancetype)initWithFrame:(CGRect)frame HtmlString:(NSString *)htmlString baseURL:(NSURL *)baseURL;
    - (instancetype)initWithFrame:(CGRect)frame UrlString:(NSString *)urlString;
    @end
    
    

    这里没有提供全能初始化方法,所以我们为了避免使用者没有使用我们调用的两个初始化方法而调用其他初始化方法,直接抛出异常

    @implementation ZSZWebVIew
    // 避免用户使用这个初始化方法
    - (instancetype)init {
     @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"必须使用ZSZWebVIew.h文件中声明的两个方法" userInfo:nil];
    }
    // 避免用户使用这个初始化方法
    - (instancetype)initWithFrame:(CGRect)frame {
    
        @throw [NSException exceptionWithName:NSInternalInconsistencyException reason:@"必须使用ZSZWebVIew.h文件中声明的两个方法" userInfo:nil];
    }
    // 本例中使用的初始化方法
    -(instancetype)initWithFrame:(CGRect)frame HtmlString:(NSString *)htmlString baseURL:(NSURL *)baseURL{
        if (self = [super initWithFrame:frame]) {
            
            self.myWebView = [[UIWebView alloc] initWithFrame:frame];
            [self.myWebView loadHTMLString:htmlString baseURL:baseURL];
            self.myWebView.delegate = self;
            [self addSubview:self.myWebView];
            
            self.progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 2)];
            [self.myWebView addSubview:self.progressView];
            [self increateProgress];
        } 
        return self;
    }
    
    - (instancetype)initWithFrame:(CGRect)frame UrlString:(NSString *)urlString{
    
        if (self = [super initWithFrame:frame]) {
         
            self.myWebView = [[UIWebView alloc] initWithFrame:frame];
            [self.myWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:urlString]]];
            self.myWebView.delegate = self;
            [self addSubview:self.myWebView];
            
            self.progressView = [[UIProgressView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 2)];
            [self.myWebView addSubview:self.progressView];
            [self increateProgress];
        }
        return self;
    }
    
    #pragma mark - 进度条累加 这里先慢慢的让进度条从0累加到0.8,在webView加载页面完成时隐藏
    static float progressValue = 0.0f;
    - (void)increateProgress
    {
        [self.progressView setProgress:progressValue animated:NO];
        progressValue += 0.0001;
        if (progressValue < 0.8) {
            [self performSelector:@selector(increateProgress) withObject:nil afterDelay:0.001];
        }else{
            [self.progressView setProgress:0.8 animated:NO];
        }
    }
    @end
    
    @interface ZSZWebVIew()<UIWebViewDelegate>
    
    #pragma mark --- webViewDelegate
    - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
        // 重新显示进度条,从0开始递增    
        self.progressView.hidden = NO;
        progressValue = 0;
        [self increateProgress];
        
        return YES;
    }
    - (void)webViewDidFinishLoad:(UIWebView *)webView {
         //  隐藏进度条
        self.progressView.hidden = YES;
    }
    - (void)webViewDidStartLoad:(UIWebView *)webView {
    }
    - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
        } 
    }
    
    

    上面的代码webView进度条逻辑就结束了
    这里我们就要考虑一个问题,我们已经让这个自定义的View监听webView了,那外面的控制器怎么玩,很简单,写一个协议就好了跟webVIew的回调方法对应上

    //  ZSZWebVIew.h
    #import <UIKit/UIKit.h>
    
    @protocol ZSZWebViewDelegate <NSObject>
    @optional
    - (BOOL)zszWebView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
    - (void)zszWebViewDidFinishLoad:(UIWebView *)webView;
    - (void)zszWebViewDidStartLoad:(UIWebView *)webView;
    - (void)zszWebView:(UIWebView *)webView didFailLoadWithError:(NSError *)error;
    @end
    
    @interface ZSZWebVIew : UIView
    @property (nonatomic, weak) id<ZSZWebViewDelegate>delegate; //这里声明注意用weak,避免保留环
    -(instancetype)initWithFrame:(CGRect)frame HtmlString:(NSString *)htmlString baseURL:(NSURL *)baseURL;
    - (instancetype)initWithFrame:(CGRect)frame UrlString:(NSString *)urlString;
    @end
    

    然后几个webView代理方法里的代码就变成这样

    #pragma mark --- webViewDelegate
    - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
        self.progressView.hidden = NO;
        progressValue = 0;
        [self increateProgress];
    // 用respondsToSelector:方法先判断delegate是否实现了这个方法
        if ([self.delegate respondsToSelector:@selector(zszWebView:shouldStartLoadWithRequest:navigationType:)]) {
            [self.delegate zszWebView:webView shouldStartLoadWithRequest:request navigationType:navigationType];
        }  
        return YES;
    }
    - (void)webViewDidFinishLoad:(UIWebView *)webView {
        self.progressView.hidden = YES;
        if ([self.delegate respondsToSelector:@selector(zszWebViewDidFinishLoad:)]) {
            [self.delegate zszWebViewDidFinishLoad:webView];
        }  
    }
    - (void)webViewDidStartLoad:(UIWebView *)webView {
        if ([self.delegate respondsToSelector:@selector(zszWebViewDidStartLoad:)]) {
            [self.delegate zszWebViewDidStartLoad:webView];
        }
    }
    - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
        if ([self.delegate respondsToSelector:@selector(zszWebView:didFailLoadWithError:)]) {
            [self.delegate zszWebView:webView didFailLoadWithError:error];
        }    
    }
    

    接下来说说重头戏,怎么在页面调OC代码调出系统相册和相机
    引入头文件 #import <JavaScriptCore/JavaScriptCore.h>
    遵循相机相册需要的两个代理@interface ZSZWebVIew()<UIWebViewDelegate,UINavigationControllerDelegate,UIImagePickerControllerDelegate>

    在webViewDidFinishLoad:中加上让js调用OC的代码

    - (void)webViewDidFinishLoad:(UIWebView *)webView {
        self.progressView.hidden = YES;
        
        // 创建JSContext
        JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
        
        // 调用系统相机 iOSCamera 就是你自定义的一个js函数名
    /*
    举个例子
    定义一个js函数在控制台打印一句话这样写
        context[@"js函数名"] = ^(){
            NSLog(@"在控制台打印一句话");
        };
    */
        context[@"iOSCamera"] = ^(){
            
            // 调用系统相机的类
            UIImagePickerController *pickerController = [[UIImagePickerController alloc] init];
            
            // 设置选取的照片是否可编辑
            pickerController.allowsEditing = YES;
            // 设置相册呈现的样式
            pickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
            // 选择完成图片或者点击取消按钮都是通过代理来操作我们所需要的逻辑过程
            pickerController.delegate = self;
            
            // 使用模态呈现相机 getCurrentViewController这个方法是用来拿到添加了这个View的控制器
            [[self getCurrentViewController] presentViewController:pickerController animated:YES completion:nil];
            
            return @"调用相机";
        };
        
        
        context[@"iOSPhotosAlbum"] = ^(){
            
            // 调用系统相册的类
            UIImagePickerController *pickerController = [[UIImagePickerController alloc] init];
            
            // 设置选取的照片是否可编辑
            pickerController.allowsEditing = YES;
            // 设置相册呈现的样式
            pickerController.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
            // 选择完成图片或者点击取消按钮都是通过代理来操作我们所需要的逻辑过程
            pickerController.delegate = self;
            
            // 使用模态呈现相册
            [[self getCurrentViewController] presentViewController:pickerController animated:YES completion:nil];
            
            return @"调用相册";
            
        };
        
        if ([self.delegate respondsToSelector:@selector(zszWebViewDidFinishLoad:)]) {
            [self.delegate zszWebViewDidFinishLoad:webView];
        }
        
    }
    
    
    /** 获取当前View的控制器对象 */
    -(UIViewController *)getCurrentViewController{
        UIResponder *next = [self nextResponder];
        do {
            if ([next isKindOfClass:[UIViewController class]]) {
                return (UIViewController *)next;
            }
            next = [next nextResponder];
        } while (next != nil);
        return nil;
    }
    

    下面的代理方法中会用OC的evaluateScript:方法去调用页面上的js函数并传图片的值

    
    #pragma mark --- 拍完照或者相册选择照片后的方法
    
    // 选择照片完成之后的代理方法
    - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info {
        
        // info是所选择照片的信息
        //  UIImagePickerControllerEditedImage//编辑过的图片
        //  UIImagePickerControllerOriginalImage//原图
        NSLog(@"info---%@",info);
        
        // 刚才已经看了info中的键值对,可以从info中取出一个UIImage对象,将取出的对象压缩上传到服务器
        UIImage *resultImage = [info objectForKey:@"UIImagePickerControllerEditedImage"];
        
        // 压缩一下图片再传
        NSData *imgData = UIImageJPEGRepresentation(resultImage, 0.001);
        
        //首先创建JSContext 对象(此处通过当前webView的键获取到jscontext)
        JSContext *context=[self.myWebView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
        NSString *encodedImageStr = [imgData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
        
        [self removeSpaceAndNewline:encodedImageStr];
        //使用模态返回到软件界面
        [[self getCurrentViewController] dismissViewControllerAnimated:YES completion:nil];
        // 这里传值给h5界面
        
        NSString *imageString = [self removeSpaceAndNewline:encodedImageStr];
        NSString *jsFunctStr = [NSString stringWithFormat:@"rtnCamera('%@')",imageString];
        [context evaluateScript:jsFunctStr];
    }
    // 图片转成base64字符串需要先取出所有空格和换行符
    - (NSString *)removeSpaceAndNewline:(NSString *)str
    {
        NSString *temp = [str stringByReplacingOccurrencesOfString:@" " withString:@""];
        temp = [temp stringByReplacingOccurrencesOfString:@"\r" withString:@""];
        temp = [temp stringByReplacingOccurrencesOfString:@"\n" withString:@""];
        return temp;
    }
    
    //点击取消按钮所执行的方法
    -(void)imagePickerControllerDidCancel:(UIImagePickerController *)picker{
        
        
        //这是捕获点击右上角cancel按钮所触发的事件,如果我们需要在点击cancel按钮的时候做一些其他逻辑操作。就需要实现该代理方法,如果不做任何逻辑操作,就可以不实现
        [[self getCurrentViewController] dismissViewControllerAnimated:YES completion:nil];
        
    }
    // 压缩图片的方法
    - (NSData *)imageWithImage:(UIImage*)image
                  scaledToSize:(CGSize)newSize;
    {
        UIGraphicsBeginImageContext(newSize);
        [image drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
        UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return UIImageJPEGRepresentation(newImage, 0.8);
    }
    

    下面是页面的代码(建一个html文件拖进工程,写上一下代码)

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8">
            <title></title>
        </head>
        <body>
    // 这里调用OC上的iOSCamera(js函数名)里的代码段
            <button onclick="iOSCamera()" style="width: 80px;height: 35px;">系统相机</button>
    // 这里调用OC上的iOSPhotosAlbum里的代码段    
            <button onclick="iOSPhotosAlbum()" style="width: 80px;height: 35px;">系统相册</button>
            
            <div id='zsz'></div>
        </body>
        <script type="application/javascript">
            // 这个js函数在OC被调用
            function rtnCamera(basedata) {
                var zsz=document.getElementById('zsz');
                zsz.innerHTML="<image style='width:200px;height:200px;' src='data:image/png;base64,"+basedata+"'>";
            };
        
        </script>
        
    </html>
    

    接下来就是我们怎么使用我们这个封装的webView了,直接上代码

    //
    //  ViewController.m
    //  ZSZWebView
    //
    //  Created by 朱松泽 on 17/2/23.
    //  Copyright © 2017年 gdtech. All rights reserved.
    //
    
    #import "ViewController.h"
    #import "ZSZWebVIew.h"
    @interface ViewController ()<ZSZWebViewDelegate>
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view, typically from a nib.
        NSURL *baseURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
        NSString *path = [[NSBundle mainBundle] pathForResource:@"webCamara" ofType:@"html"];
        NSString *html = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
        ZSZWebVIew *zszWebView = [[ZSZWebVIew alloc] initWithFrame:self.view.frame HtmlString:html baseURL:baseURL];
    //    ZSZWebVIew *zszWebView = [[ZSZWebVIew alloc] init]; // 用这个方法初始化就抛出异常
    //    ZSZWebVIew *zszWebView = [[ZSZWebVIew alloc] initWithFrame:self.view.frame]; // 用这个方法初始化也抛出异常
        zszWebView.delegate = self;
        [self.view addSubview:zszWebView];
        
    }
    
    - (BOOL)zszWebView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
        
        NSLog(@"shouldStartLoadWithRequest:执行完封装的代码才来到这里");
        return YES;
    }
    
    - (void)zszWebViewDidFinishLoad:(UIWebView *)webView {
        NSLog(@"zszWebViewDidFinishLoad:执行完封装的代码才来到这里");
    }
    
    -(void)zszWebView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
    }
    -(void)zszWebViewDidStartLoad:(UIWebView *)webView {
    }
    
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    
    @end
    
    

    下面是本例的效果图,如图点击webView上的按钮跳转到相机或相册,并把图片传到webView的页面上

    ZSZWebView例子效果截图.PNG

    Demo从这里下载:https://github.com/ZSZ1994/ZSZWebView

    相关文章

      网友评论

      • 丘天:测试了可以上传图片,楼主,我这边点击按钮之前需先长按下按钮再点击按钮才能弹出选择相册还是相机的框,如果不先长按直接点击按钮没有任何反应,这是怎么回事啊
      • 若幹年後:楼主 是否可以不调用 js rtnCamera的方法 把图片直接加载到webView 页面上
        若幹年後:@若幹年後 比如我调用js的打开相册方法 调用系统相册 选择相册之后直接把图片加载到h5页面上。 不走rtnCamera方法
        若幹年後:@三点水老木头 楼主 怎么直接加载图片 给点思路!谢谢
        三点水老木头:@若幹年後 webView直接加载图片是可以,这传值是要前端的人能在rtnCamera方法之后去做处理图片的逻辑,比如他要修改布局或者裁剪图片之类
      • zziazm:你好,如果传多张图片的话应该怎么做呢
        三点水老木头:这里是通过base64字符串传图片的,多张图片由多个base64字符串组成,这多个字符串之间跟前端约定一个key(Seq_Seq)相连就好之后再根据key分开。例如:base1 + Seq_Seq + base2 +Seq_Seq + base3 ...
      • 2cc6bd480f3e:非常有用 可以可以
      • 绍清_shao:二哥,这波稳:+1:
        三点水老木头:@葚嚒Shao :sweat:清,你又调皮了
      • 男人的担当:很好,可以一步步跟着写,并且有效果图了。只是进度条没看到啊。
        男人的担当:@三点水老木头 好的
        三点水老木头:@e5d7910de0cf 在最上面蓝色进度条,可能比较小~~~你把进度条高度增加下应该能看到

      本文标题:iOS OC与JS交互+调用系统相册和相机并把图片传至页面+we

      本文链接:https://www.haomeiwen.com/subject/dvubwttx.html