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

时间:2024-04-15 18:23:56

1、最新最详细代码都在github上,欢迎下载。github地址:https://github.com/honeycao/ACMediaFrame
2、代码细节问题可以参考简书文章:http://www.jianshu.com/p/9ff1e8e68a21
3、框架主要功能:
3.1、本地图片视频选择、拍照录制等全封装使用
3.2、主体是一个展示的页面,可添加到任何地方,同时处理好了一切布局。
3.3、选择图片和图片预览等用到了TZImagePickerController和MWPhotoBrowser两个第三方
3.4、自定义媒体model,存储图片、视频上传数据类型,如:NSData或视频路径。
3.5、结果:添加该框架到任意页面之后不再需要做其他操作,只需要写上文件上传的网络请求即可。

 

写在前面的话

  • 更新:框架上的东西最近有所改动,更具体的使用和功能可以参考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 ,你的点赞是我最大的动力)


作者:大都市里小刁民
链接:https://www.jianshu.com/p/9ff1e8e68a21
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。