iOS 媒体库完整流程封装:图片视频选择、展示布局、上传等(一)

写在前面的话

  • 更新:框架上的东西最近有所改动,更具体的使用和功能可以参考GitHub源代码
  • 本文章是基于自己封装的一个媒体框架来写的,比较粗糙,时间短,待改进。具体内容可以到github中查看:ACMediaFrame
  • 下面没有涉及框架的使用,使用比较简单方便,可以看github就都清楚了,主要是罗列一些具体功能点,和重点的解析(其实都算不上重点勒。都是简单的东西,然后加以改进完善而已,所以看起来应该没什么难度的)
  • 框架主体:本地选择图片视频、拍摄图片视频;布局展示图片视频,可删除;直接存储了供上传的数据类型,所以距离上传就一步之遥(发送请求而已)。
ACMediFrame展示.gif

主要内容

本媒体库系列包括:其一,本地图片、视频、拍照、录像等的选择;其二,媒体文件布局展示操作等;其三:媒体文件上传操作简易化。

一、媒体文件选择

大部分项目都有图片选择和拍照等功能,更甚至录像和视频等,对于多次用到这些功能,借此机会进行基础封装。选择照片用到了第三方TZImagePickerController进行多图选择。

1、TZImagePickerController的简单使用

这个框架主要用来选择多张图片,下面是简单使用的代码,也是媒体系列框架所用到的,更详细可以点击链接去查看。对于其代理方法中获取的图片或视频的处理,在后面会详细介绍。

github通道:TZImagePickerController

//初始化 并设置代理 和 最大选择张数
TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:9 delegate:self];
///是否 在相册中显示拍照按钮
imagePickerVc.allowTakePicture = NO;
///是否可以选择显示原图
imagePickerVc.allowPickingOriginalPhoto = NO;
///是否 在相册中可以选择视频
imagePickerVc.allowPickingVideo = YES;
[self presentViewController:imagePickerVc animated:YES completion:nil];

#pragma mark - TZImagePickerController Delegate

//处理从相册单选或多选的照片
- (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingPhotos:(NSArray<UIImage *> *)photos sourceAssets:(NSArray *)assets isSelectOriginalPhoto:(BOOL)isSelectOriginalPhoto{
  
}

///选取视频后的回调
- (void)imagePickerController:(TZImagePickerController *)picker didFinishPickingVideo:(UIImage *)coverImage sourceAssets:(id)asset {
  
}
2、系统方法UIImagePickerController的使用
  • 基础枚举类型解析
//picker资源类型
typedef NS_ENUM(NSInteger, UIImagePickerControllerSourceType) {
    UIImagePickerControllerSourceTypePhotoLibrary, // 媒体库(所有媒体文件夹:视频和图片)
    UIImagePickerControllerSourceTypeCamera, //相机
    UIImagePickerControllerSourceTypeSavedPhotosAlbum //相册
}
//相机捕获类型
typedef NS_ENUM(NSInteger, UIImagePickerControllerCameraCaptureMode) {
    UIImagePickerControllerCameraCaptureModePhoto, //拍照 (默认)
    UIImagePickerControllerCameraCaptureModeVideo //录像
}
//进入相册转场动画
typedef NS_ENUM(NSInteger, UIModalTransitionStyle) {
    UIModalTransitionStyleCoverVertical, //默认,竖直方向推出
    UIModalTransitionStyleFlipHorizontal,//水平方向翻转
    UIModalTransitionStyleCrossDissolve,//溶解,没有太多动画
    UIModalTransitionStylePartialCurl //上下翻页
};
  • 打开相机:拍照或录像
/** 打开相机 */
- (void)openCamera {
    UIImagePickerControllerSourceType sourceType = UIImagePickerControllerSourceTypeCamera;

    if ([UIImagePickerController isSourceTypeAvailable: UIImagePickerControllerSourceTypeCamera]){
        UIImagePickerController *picker = [[UIImagePickerController alloc] init];
        picker.delegate = self;
        //设置拍照后的图片可被编辑
        picker.allowsEditing = YES;
        picker.sourceType = sourceType;
      /* 如果是录像
      picker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModeVideo;
        picker.videoQuality = UIImagePickerControllerQualityTypeMedium; //录像质量
        picker.videoMaximumDuration = 600.0f; //录像最长时间
        */
        [self presentViewController:picker animated:YES completion:nil];
    }else{
      //不支持拍照或模拟器系列
    }
}
  • 相册或所有媒体文件夹
- (void)openPhotos {
    UIImagePickerController *picker = [[UIImagePickerController alloc] init];
    picker.delegate = self;
    //转场动画
    picker.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
    picker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
  /* 选择所有媒体文件夹
  picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary
  */
    picker.mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:picker.sourceType];
    picker.allowsEditing = YES;
    [self presentViewController:picker animated:YES completion:nil];
}
  • 代理方法

    • 代理方法回调的 info 字典key解析
    //媒体类型 分为: @"public.movie" 和 @"public.image"
    UIImagePickerControllerMediaType    
      
    //原图 (视频和录像都没有)  
    UIImagePickerControllerOriginalImage  
      
    //编辑后的图片(前提是设置了 picker.allowsEditing = YES ,不然没有这个),视频和录像没有这个
    UIImagePickerControllerEditedImage 
      
    //视频录像的URL
    UIImagePickerControllerMediaURL    
      
    //原件的URL,从本地选取的才有,也就是说拍照和录像都不存在这个  
    UIImagePickerControllerReferenceURL  
    
#pragma mark - UIImagePickerController Delegate
  
///拍照、选视频图片、录像 后的回调(这种方式选择视频时,会自动压缩,但是很耗时间)
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info {
  //todo: 后面有具体处理方法
}

二、布局、展示

布局的话,也不是太复杂,看图也知道我的布局,具体内容请看github中源码,反正都是直接封装好的。至于布局,用到了第三方MWPhotoBrowser,可以大图显示图片和视频。

1、MWPhotoBrowser的简单使用

github通道:MWPhotoBrowser

NSMutableArray *photos = [NSMutableArray array];
//初始化 并 设置代理
MWPhotoBrowser *browser = [[MWPhotoBrowser alloc] initWithDelegate:self];
//是否显示活动按钮(分享等操作,默认为 YES)
browser.displayActionButton = NO;
// 是否一直显示导航栏控制器(默认为NO,点击图片消失,再点击又出现,如果有图片名,那么和导航栏一样)
browser.alwaysShowControls = NO;
//是否在每张图片右上角显示一个图片选中的按钮(默认NO)
browser.displaySelectionButtons = NO;
//是否显示底部工具条的左右箭头,多张图才有效果,箭头也就是用来翻页的(默认NO)
browser.displayNavArrows = NO;
//图片填满屏幕,依据最小边来定(默认YES)
browser.zoomPhotosToFill = YES;
browser.startOnGrid = NO;
browser.enableGrid = YES;

//添加图片数组:数据必须都是 MWPhoto 类型的
//添加本地图片
[photos addObject:[MWPhoto photoWithURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"photo2l" ofType:@"jpg"]]]];
//添加url图片
[photos addObject:[MWPhoto photoWithURL:[NSURL URLWithString:@"http://farm4.static.flickr.com/3629/3339128908_7aecabc34b.jpg"]]];
//添加视频
//视频封面图
MWPhoto *video = [MWPhoto photoWithURL:[NSURL URLWithString:@"https://scontent.cdninstagram.com/hphotos-xpt1/t51.2885-15/e15/11192696_824079697688618_1761661_n.jpg"]];
//视频内容url
video.videoURL = [[NSURL alloc] initWithString:@"https://scontent.cdninstagram.com/hphotos-xpa1/t50.2886-16/11200303_1440130956287424_1714699187_n.mp4"];
[photos addObject:video];

//设置大图时 首先显示的图片(也就是photos数组的第几张,一定要在设置了photos数组之后设置,不然是没有作用的)
[browser setCurrentPhotoIndex:1];
//必须是 push 
[self.navigationController pushViewController:browser animated:YES];
2、代理方法
#pragma mark - <MWPhotoBrowserDelegate>
//图片总数
- (NSUInteger)numberOfPhotosInPhotoBrowser:(MWPhotoBrowser *)photoBrowser {
    return self.photos.count;
}
//下标对应的图片数据源
- (id <MWPhoto>)photoBrowser:(MWPhotoBrowser *)photoBrowser photoAtIndex:(NSUInteger)index {
    if (index < self.photos.count) {
        return [self.photos objectAtIndex:index];
    }
    return nil;
}

三、上传等图片信息处理

通过前面步骤得到了数据,并知道怎么显示了,那么接下来就是数据处理和上传的准备。为此单独准备了一个model来存储媒体信息。通过这个模型我们就可以分别得到图片显示的image,和视频显示的url,最主要也存储好了上传的类型,图片上传NSData,视频较大一般都是上传本地路径,不会直接上传NSData。

#import <Photos/Photos.h>

@interface ACMediaModel : NSObject

/** 媒体名字 */
@property (nonatomic, copy) NSString *name;

/** 媒体上传格式 图片是NSData,视频主要是路径名,也有NSData */
@property (nonatomic, strong) id uploadType;

//兼容 MWPhotoBrowser 的附加属性

/** 媒体照片 */
@property (nonatomic, strong) UIImage *image;

/** iOS8 之后的媒体属性 */
@property (nonatomic, strong) PHAsset *asset;

/** 是否属于可播放的视频类型 */
@property (nonatomic, assign) BOOL isVideo;

/** 视频的URL */
@property (nonatomic, strong) NSURL *mediaURL;

@end

四、根据得到的媒体数据转化存储为自定义的ACMediaModel模型

这个算的上是一个比较关键的数据处理步骤了,分为2种,分别是UIImagePickerControllerTZImagePickerController得到的数据处理,涉及到PHAsset的解析和URL、Image的处理

​ PHAsset是iOS8之后<Photos/Photos.h>库下的API,对于PHAsset具体解析可以参考:ALAsset/PHAsset 中的图片和视频文件,比较详细。

​ 在封装的方法中,确并没有完全用上上面的方法,主要是PHAsset解析视频时,异步加载回调需要等待一段时间,这个就不太符合要求了,所以换了如下方法来处理

/**
 根据 PHAsset 来获取 媒体文件(视频或图片)相关信息:文件名、文件上传类型(data 或 path)

 @param asset  PHAsset对象
 @param completion 成功回调
 */
+ (void)getMediaInfoFromAsset: (PHAsset *)asset completion: (void(^)(NSString *name, id pathData))completion {
    if (!asset) {
        return;
    }
    NSString *mediaName;
    __block NSData *data;
    
    if (asset.mediaType == PHAssetMediaTypeImage) {
        //图片文件名
        mediaName = [self getMediaNameWithPHAsset:asset extensionName:@"Image.png"];
        PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
        options.version = PHImageRequestOptionsVersionCurrent;
        //返回图片的质量类型 (效率高,质量低)
        options.deliveryMode = PHImageRequestOptionsDeliveryModeFastFormat;
        //同步请求获取iCloud图片(默认为NO)
        //options.synchronous = YES;
        
        [[PHImageManager defaultManager] requestImageDataForAsset:asset options:options resultHandler:^(NSData * _Nullable imageData, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info) {
            data = [NSData dataWithData:imageData];
            
            if (completion && data.length > 0) {
                completion(mediaName, data);
            }
        }];
    }
    else if (asset.mediaType == PHAssetMediaTypeVideo ||
             asset.mediaSubtypes == PHAssetMediaSubtypePhotoLive) {
        //视频文件名
        mediaName = [self getMediaNameWithPHAsset:asset extensionName:@"IMG.MOV"];
        
        NSString *videoPath = [NSTemporaryDirectory() stringByAppendingString:mediaName];
        BOOL success = [[NSFileManager defaultManager] fileExistsAtPath:videoPath];
        //当前处理方式:本地的视频 直接返回路径,非本地的也不存储到本地,直接返回data
        if (success) {
            !completion ?  : completion(mediaName, videoPath);
        }else{
            NSData *videoData = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:videoPath]];
            !completion ?  : completion(mediaName, videoData);
        }
    }
}

​ 上面的图片解析是根据PHAsset方法来的,但是刚开始出现同时解析多张,回调缺少了数据的问题,后来没怎么修改,但是这个问题又不存在了,所以到时候看看需要需要也把这个方法替换掉。。。

​ 经过之前一系列数据的处理,基本都属于ACMediaModel属性范围,对此就基本完成了显示的媒体数据源的添加,根据这个ACMediaModel,我们可以展示图片视频、删除并且上传,对于上传我们所需要添加到request的header中去的,就是ACMediaModel中的uploadType属性。

五、结语

  • 一开始讲到了媒体文件选择,这个都很基础,列出来做个参考
  • 其次是涉及到TZImagePickerControllerMWPhotoBrowser两个第三方库的基本用法,以及代理方法,但是代理方法中怎么处理,也一直没说,简单点就是通过第四步的方法处理PHAsset或者info,然后转化为Model存储、展示等。具体可以看代码中。
  • 写这个的初衷是连续多次用到这个知识,每次都去写布局、方法处理,觉得很麻烦,所以就想着写到一起来,虽然PHAsset在这里没用到详细,但是一开始研究的时候是翻阅了很多相关知识,而且研究下去也是有很多新的知识,只是最后发现有了不足的地方才改换简单的方法。
  • 在本文章中并没有说太多框架使用的方法,只是对其中一些方法的罗列和简单分析。具体框架使用可以到github中查看。
  • github地址:ACMediaFrame 还是那句话:写的不好,欢迎探讨、指正。I am a rookie ,I am not God (有建议或想法请q:331864805 ,你的点赞是我最大的动力)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容