封装的效果到底好不好,先来看看效果图。自我感觉良好,同微信图片多选模块项目不会差到哪里,只是暂时没有选择原图功能,demo中默认选择高清图像。这个功能实现起来也比较简单,只要更改图片尺寸无论是高清图像还是缩略图都可以轻松获取,后面代码分析中会说明。由于实际开发中,项目需求很少用到是否选择原图这一模块,所以综合考虑就没有增添这一模块。Demo下载地址:https://github.com/ZhengYaWei1992/ZWChooseMultiImages
首先看一下外部调用实现代码。
ZWPhotoGroupsViewController *groupsVC = [[ZWPhotoGroupsViewController alloc]init];
UINavigationController *groupsNav = [[UINavigationController alloc]initWithRootViewController:groupsVC];
[[ZWImageHelper shareImageHelper]getAllImageActionWithCollection:nil];
[self presentViewController:groupsNav animated:YES completion:nil];
选中的照片资源主要是放在[ZWImageHelper shareImageHelper].phassetChoosedArr里面。
[self.chooseImages addObjectsFromArray:[ZWImageHelper shareImageHelper].phassetChoosedArr];
先说一下整体架构吧。主要分为四个模块,分别见Demo中的ZWChooseMultiImages文件夹下的相关文件,PhotosList主要负责展示特定分组下的所有照片,PhotosGroups主要负责显示所有照片分组,FullImage主要负责显示预览照片(支持缩放手势),ZWImageHelper主要是通过单例的形式保存特定分组下的照片以及选中的照片。PHAsset+addFlag主要是为PHAsset扩展chooseFlag属性,记录照片是否是被选中的资源。ZWChooseMultiImagesConfig是一个配置文件,这里面可以更改界面相关样式,如字体颜色和大小、上下view的背景颜色、以及可以选择最多图片数量的上限。
关于获取如何获取系统的照片资源。获取系统的图片资源主要是使用<Photos/Photos.h>这个框架文件。获取特定分组里面的所有资源图片。
//获取一个相册里面所有照片的操作
- (void)getAllImageActionWithCollection:(PHFetchResult *)result {
if (result) {
for (id obj in result) {//获取标识符
[self.phassetArr addObject:obj];
}
}else {
// 获取所有资源的集合,并按资源的创建时间排序
///获取资源时的参数
PHFetchOptions *options = [[PHFetchOptions alloc] init];
options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
//表示一系列的资源的集合,也可以是相册的集合
PHFetchResult *assetsFetchResults = [PHAsset fetchAssetsWithOptions:options];
for (id obj in assetsFetchResults) {//获取标识符
[self.phassetArr addObject:obj];
//============================================
//重点说明:如果不采用标志符方式,而是按照如下方式,读取太慢会导致拖动卡顿
// PHAsset *asset = obj;
// if ([asset.localIdentifier isKindOfClass:[NSString class]]) {
// [self.phassetArr addObject:asset.localIdentifier];
// }
}
}
}
获取所有包含照片资源的分组列表。
- (void)getAllKindImagesAction {
PHFetchResult *kindAlbum = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
for (PHAssetCollection *colllection in kindAlbum) {
NSString *kindName = colllection.localizedTitle;
PHFetchResult *result = [PHAsset fetchAssetsInAssetCollection:colllection options:nil];
NSString *imageCount = [@(result.count) stringValue];
NSDictionary *resultDic = @{@"count" : imageCount, @"title" : kindName, @"result" : result};
//过滤掉照片个数为0的分组
NSString *countStr = resultDic[@"count"];
if (![countStr isEqualToString:@"0"]) {
[self.kindArr addObject:resultDic];
}
}
}
从选中的图片数组中读取单张图片信息。
- (void)getAllKindImagesAction {
PHFetchResult *kindAlbum = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
for (PHAssetCollection *colllection in kindAlbum) {
NSString *kindName = colllection.localizedTitle;
PHFetchResult *result = [PHAsset fetchAssetsInAssetCollection:colllection options:nil];
NSString *imageCount = [@(result.count) stringValue];
NSDictionary *resultDic = @{@"count" : imageCount, @"title" : kindName, @"result" : result};
//过滤掉照片个数为0的分组
NSString *countStr = resultDic[@"count"];
if (![countStr isEqualToString:@"0"]) {
[self.kindArr addObject:resultDic];
}
}
}
从选中的图片资源中读取单张图片信息,并任意设置图片大小。
/*
相关说明:asset,图像对应的 PHAsset。
targetSize,需要获取的图像的尺寸,如果输入的尺寸大于资源原图的尺寸,则只返回原图。需要注意在 PHImageManager 中,所有的尺寸都是用 Pixel 作为单位(Note that all sizes are in pixels),因此这里想要获得正确大小的图像,需要把输入的尺寸转换为 Pixel。如果需要返回原图尺寸,可以传入 PhotoKit 中预先定义好的常量?PHImageManagerMaximumSize,表示返回可选范围内的最大的尺寸,即原图尺寸。
contentMode,图像的剪裁方式,与?UIView 的 contentMode 参数相似,控制照片应该以按比例缩放还是按比例填充的方式放到最终展示的容器内。注意如果 targetSize 传入?PHImageManagerMaximumSize,则 contentMode 无论传入什么值都会被视为?PHImageContentModeDefault。
options,一个?PHImageRequestOptions 的实例,可以控制的内容相当丰富,包括图像的质量、版本,也会有参数控制图像的剪裁,下面再展开说明。
resultHandler,请求结束后被调用的 block,返回一个包含资源对于图像的 UIImage 和包含图像信息的一个 NSDictionary,在整个请求的周期中,这个 block 可能会被多次调用,关于这点连同 options 参数在下面展开说明。
*/
- (void)displayCellWithAssetString:(PHAsset *)asset {
__weak ZWChoosedCollectionViewCell *weakSelf = self;
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
options.synchronous = YES;
options.resizeMode = PHImageRequestOptionsResizeModeFast;
//小图,大小自己定义
// CGSize imageSize = CGSizeMake(CELL_COLLECTION_WIDTH - 6, CELL_COLLECTION_WIDTH - 6);
// [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:imageSize contentMode:PHImageContentModeAspectFill options:options resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
// weakSelf.chooseImage.image = result;
// }];
//高清图
[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeAspectFill options:options resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
weakSelf.chooseImage.image = result;
}];
}
另外有一点非常值得说明的是关于界面跳转问题,因为第一次进入图片浏览器会发现并不是直接就跳转到照片分组列表界面,而是跳转到特定分组下的某个分组内的图片选择界面。这种跳转方式可能有n种解决方案,但是感觉微信的这种跳转更为合理(模态进入照片选择模块,模态离开照片选择界面)。见过其他的照片多选第三方框架,但是感觉很多在这一块做得都不是很好。这个demo中,照片多选模块的进入和离开方式和微信上是一样的。其实很简单的,只要在展示照片分组的viewDidLoad方法中多加上一句代码即可,剩下的就是常规的模态进入和离开。
//首次进入直接跳转到下一界面
[self.navigationController pushViewController:self.listVC animated:NO];
天机了这句代码之后,就能保证首次进入照片多选模块后,直接进入第二界面。但是要格外注意animated参数设置为NO,否则实际运行过程中会显示第一个界面。
因为这个照片多选模块的封装代码量还是不少的,就简单说着几个重点吧,其余的都相对而言比较简单。具体可以参照我github上的demo。