iOS OC与JS交互+调用系统相册和相机并把图片传至页面+webView进度条简单封装

先说下这篇文章的预期目标,就是利用原生库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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,103评论 4 62
  • 一、简介 近两年随着HTML5的迅速发展与日趋成熟,越来越多的移动开发者选择使用HTML5来进行混合开发,不仅节约...
    RainyGY阅读 1,870评论 1 12
  • 诗/昂格伦 一片水 一片天 一棵树 倒影在其间 虬枝独舞 顾影自怜 风儿轻抚 月儿相伴 云晕红天边 曾经孤苦 曾经...
    昂格伦阅读 140评论 2 4
  • 18岁 一个惊喜又惊悚的年纪 一个年少与年老的分水岭 一条年华与华年的分界线 而这条线叫——成长 来不及拥抱清晨 ...
    心心稀语阅读 336评论 0 0