封裝的效果到底好不好,先來看看效果圖。自我感覺良好,同微信圖片多選模塊項目不會差到哪里,只是暫時沒有選擇原圖功能,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。