一.引言
最近項目中要用到相冊和選取截圖,但是系統(tǒng)的不太符合設計要求贮尉,所以就自己實現(xiàn)了一個.
首先說說做前普及的知識.在iOS中,關于相冊的系統(tǒng) API有兩個,準確來說其實是一個.分別為AssetsLibrary和Photos莹汤,AssetsLibrary框架是iOS7之前的關于相冊的系API,而在iOS8的時候由于AssetsLibrary框架的一些問題.所以蘋果推出了新的API:Photos颠印,知道iOS9的時候直接已經(jīng)廢棄了AssetsLibrary框架纲岭,但是我們在項目還要兼容iOS7,所以要對這兩個分別來配置下.
二. AssetsLibrary框架
AssetsLibrary框架是 iOS7之前的相冊系統(tǒng)框架,iOS9已被廢棄,相似于一個數(shù)據(jù)集合,而想要獲得某類型的照片或者照片組就得按照相應的規(guī)則去遍歷數(shù)據(jù)集合,來獲得想要的.這樣的性能是不是太好.接下來看看主要組成該框架的類.
- AssetsLibrary:包括相冊資源集合,以及篩選,存入,權限等api類.此類大概分三大塊,一塊是保存圖片到數(shù)據(jù)集合,一塊是讀取數(shù)據(jù)集合中的圖片,最后一塊是用戶相關權限:
保存類api 是以write開頭的一些 api
- (void)writeImageToSavedPhotosAlbum:(CGImageRef)imageRef
orientation:(ALAssetOrientation)orientation
completionBlock:(ALAssetsLibraryWriteImageCompletionBlock)completionBlock;
保存一個圖片到相冊:
imageRef:保存的圖片, CGImageRef類型,可由[UIImage CGImage]獲得
orientation:保存圖片時,圖片的方向,可由[UIImage imageOrientation]或得,枚舉:
ALAssetOrientationUp 上
ALAssetOrientationDown 下
ALAssetOrientationLeft 左
ALAssetOrientationRight 右
ALAssetOrientationUpMirrored 水平上翻轉
ALAssetOrientationDownMirrored 水平下翻轉
ALAssetOrientationLeftMirrored 豎直左翻轉
ALAssetOrientationRightMirrored 豎直右翻轉
completionBlock:回調(diào) block, 定義為:typedef void (^ALAssetsLibraryWriteImageCompletionBlock)(NSURL *assetURL, NSError *error)
成功回調(diào)得到 iamge 的 url.這里的 url 是本地的,可以用它來檢索 image,error: 回調(diào)到錯誤信息.
其他保存 aip 類似.
讀取類 api,這說下常用的遍歷ALAssetsLibrary集合的.
- (void)enumerateGroupsWithTypes:(ALAssetsGroupType)types
usingBlock:(ALAssetsLibraryGroupsEnumerationResultsBlock)enumerationBlock
failureBlock:(ALAssetsLibraryAccessFailureBlock)failureBlock;
遍歷資源集合獲得篩選到的相冊:
types:刪選條件,枚舉:
ALAssetsGroupLibrary 所有的相冊(寶庫本地和 iTunes 的)
ALAssetsGroupAlbum 同步得到iTunes的和創(chuàng)建的(不包括共享的)
ALAssetsGroupEvent 同步到 iTunes 的(包括相機導入的)
ALAssetsGroupFaces 同步 iTunes 的
ALAssetsGroupSavedPhotos 所有相冊(包括相機導入的)
ALAssetsGroupPhotoStream itunes 同步的(包括共享的)
ALAssetsGroupAll 所有可見的相冊
enumerationBlock:成功回調(diào) block, 定義:typedef void (^ALAssetsLibraryGroupsEnumerationResultsBlock)(ALAssetsGroup *group, BOOL *stop)
獲得 group: 每個圖片或視屏組成的相冊,stop: 停止遍歷的標示
failureBlock:失敗的回調(diào):定義:typedef void (^ALAssetsLibraryAccessFailureBlock)(NSError *error)
error: 錯誤信息.
權限設置類:
+ (ALAuthorizationStatus)authorizationStatus;
類方法,返回值一個ALAuthorizationStatus的枚舉類型,來表明當時的用戶權限狀況.
ALAuthorizationStatusNotDetermined 用戶還未做出選擇
ALAuthorizationStatusRestricted 用戶權限設置(如家長模式)
ALAuthorizationStatusDenied 明確選擇過不允許獲得權限
ALAuthorizationStatusAuthorized 同意獲取
- ALAssetsGroup:資源中某個相冊資源,可以獲取相冊中具體照片或者視屏,添加或者獲取詳細相冊信息,列出幾個常用主要的方法:
(1):獲取相冊數(shù)量:
- (NSInteger)numberOfAssets;
獲取數(shù)量是和你的刪選條件有關filter.
(2):刪選條件:
- (void)setAssetsFilter:(ALAssetsFilter *)filter;
給定一個刪選條件.
filter:刪選的一個條件,ALAssetsFilter類型的類方法
+ (ALAssetsFilter *)allPhotos; 所有的圖片
+ (ALAssetsFilter *)allVideos; 所有的視屏
+ (ALAssetsFilter *)allAssets; 所有的資源(包括圖片和視頻)
(3):相冊封面照片:
- (CGImageRef)posterImage;
獲得相冊的封面照片,該返回的圖片方向是正確的.
(4):返回相冊的一些相關屬性:
- (id)valueForProperty:(NSString *)property;
property:相冊相關的一些屬性,定義了常量:
extern NSString *const ALAssetsGroupPropertyName 相冊的名稱
extern NSString *const ALAssetsGroupPropertyType 相冊的類型
extern NSString *const ALAssetsGroupPropertyPersistentID 相冊的標示 id
extern NSString *const ALAssetsGroupPropertyURL 相冊的 url
(5):獲取相冊中的資源:
- (void)enumerateAssetsUsingBlock:(ALAssetsGroupEnumerationResultsBlock)enumerationBlock;
enumerationBlock:遍歷資源回調(diào)的 block, 定義為:typedef void (^ALAssetsGroupEnumerationResultsBlock)(ALAsset *result, NSUInteger index, BOOL *stop);
result:一個圖片或者資源的類,包含了資源的一些信息.
index:遍歷相冊時result 對應的下標位置.
stop:對遍歷的操作控制.如果有特殊條件可以在某個位置停止獲取資源.
- ALAsset:相冊中每個對象,包含了相片或者視頻的一些信息.可以寫入或者獲取到一些資源.
(1):返回一個ALAssetRepresentation對象,一個ALAsset對像默認有一個ALAssetRepresentation對象,用他來獲取更加詳細的每個具體資源的信息,如:原圖,大小,字節(jié)等.
- (ALAssetRepresentation *)defaultRepresentation;
(2):獲得縮略圖:
- (CGImageRef)thumbnail;
該方法獲得相對質量不高的縮略圖,他是對原圖進行從最中間進行正方形的裁剪.改圖不是按比例縮小.圖片的方向的正確的.
(3):獲得縮略圖:
- (CGImageRef)aspectRatioThumbnail;
和上面的方法得到的都是縮略圖,但是不同的是他是按圖片原先的比例按比例縮小的
(4).保存圖片到相冊:
- (void)writeModifiedImageDataToSavedPhotosAlbum:(NSData *)imageData metadata:(NSDictionary *)metadata completionBlock:(ALAssetsLibraryWriteImageCompletionBlock)completionBlock;
保存圖片到相冊:
imageData:要保存的圖片流,
metadata:保存圖片的元數(shù)據(jù),一般為 nil,如果和前面的 imageData 沖突的話,會覆蓋掉前面的信息,
completionBlock:保存完的 block 回調(diào),定義為:typedef void (^ALAssetsLibraryWriteImageCompletionBlock)(NSURL *assetURL, NSError *error),
assetUrl: 保存成功后映射該相片的 url
error: 保存失敗信息.
- ALAssetRepresentation:對 ALAsset 的封裝,包含了每個相冊的一些詳細信息,可以用defaultRepresentation獲得默認的那個該對象.這里要注意的是,如果是相冊的話,默認是一個該對象,而如果是相機拍照的情況下.默認是有兩個該對象的,一個包含了RAW 的信息,一個包含了JPEG的信息.
三.Photos 框架
Photos 是 iOS8時蘋果新推出的一個關于系統(tǒng)相冊的新框架.改框架應該跟AssetsLibrary的處理不一樣,他不是去根據(jù)條件遍歷數(shù)據(jù)資源,而是根據(jù)條件直接獲得指定的資源.所以相比而言,更加高效和完整.下來看看主要組成該框架的類.
- PHAssetCollection:PHCollection的子類,相冊分類,一系列的相冊.如:最近刪除.精選等.常用 api:
(1).該相冊的類型
assetCollectionType;
PHAssetCollectionType的枚舉值:
PHAssetCollectionTypeAlbum 相冊
PHAssetCollectionTypeSmartAlbum 智能相冊
PHAssetCollectionTypeMoment 時刻
(2).該相冊的子類型
assetCollectionSubtype;
PHAssetCollectionSubtype枚舉類型:
常規(guī)的子類型
PHAssetCollectionSubtypeAlbumRegular 常規(guī)的
PHAssetCollectionSubtypeAlbumSyncedEvent 使用 iTunes 同步操作過來的相冊
PHAssetCollectionSubtypeAlbumSyncedFaces 使用 iTuens同步操作過來的人物相冊
PHAssetCollectionSubtypeAlbumSyncedAlbum 使用iTunes 同步的所有相冊
PHAssetCollectionSubtypeAlbumImported 從外界導入的相冊
經(jīng)分享的子類型
PHAssetCollectionSubtypeAlbumMyPhotoStream 從相冊分享得到
PHAssetCollectionSubtypeAlbumCloudShared 從 cloud 分享得到
智能相冊子類型
PHAssetCollectionSubtypeSmartAlbumGeneric 通用的
PHAssetCollectionSubtypeSmartAlbumPanoramas 全景
PHAssetCollectionSubtypeSmartAlbumVideos 視屏
PHAssetCollectionSubtypeSmartAlbumFavorites 收藏
PHAssetCollectionSubtypeSmartAlbumTimelapses 延時視屏,也會在PHAssetCollectionSubtypeSmartAlbumVideos在出現(xiàn)
PHAssetCollectionSubtypeSmartAlbumAllHidden 隱藏的
PHAssetCollectionSubtypeSmartAlbumRecentlyAdded 最近添加
PHAssetCollectionSubtypeSmartAlbumBursts 連拍
PHAssetCollectionSubtypeSmartAlbumSlomoVideos Slomo是slow motion的縮寫,高速攝影慢動作解析
PHAssetCollectionSubtypeSmartAlbumUserLibrary 用戶所有的資源
PHAssetCollectionSubtypeSmartAlbumSelfPortraits 所有前置攝像頭拍的照片和視屏
PHAssetCollectionSubtypeSmartAlbumScreenshots 所有的截屏圖
不關心子類型時的全部資源
PHAssetCollectionSubtypeAny = NSIntegerMax
(3).獲得相冊的集合資源
+ (PHFetchResult<PHAssetCollection *> *)fetchAssetCollectionsWithType:(PHAssetCollectionType)type subtype:(PHAssetCollectionSubtype)subtype options:(nullable PHFetchOptions *)options;
返回一個相冊集合資源,集合里面是PHAssetCollection類型的對象.
type:相冊類型, PHAssetCollectionType類型的枚舉,
subtype:子類型, PHAssetCollectionSubtype的枚舉,
PHFetchOptions: PHFetchOptions的一個實例,可以為空,只要是為了對獲得資源做一些配置和排序等.可以為 nil.
- PHFetchOptions:對使用 PHAsset, PHCollection, PHAssetCollection, 和 PHCollectionLis 的方法時出入的參數(shù),主要對獲取到資源做一些配置和排序等,一般為 nil, 默認使用系統(tǒng)的.
(1).排序
sortDescriptors;
(2).是否顯示隱藏的相冊,默認不顯示
includeHiddenAssets
(3).獲取到相冊的類型
includeAssetSourceTypes;
PHAssetSourceType類型的枚舉,默認PHAssetSourceTypeNone
PHAssetSourceTypeNone 都沒有,就獲得到就是常規(guī)的
PHAssetSourceTypeUserLibrary 用戶所有的
PHAssetSourceTypeCloudShared 分享的
PHAssetSourceTypeiTunesSynced iTunes 同步的
- PHFetchResult:相冊資源,包括相冊中圖片的數(shù)量和獲取.看些常用 api.
(1).相冊中圖片的數(shù)量
count
(2).遍歷得到相冊資源中每個相冊組的信息.
- (void)enumerateObjectsUsingBlock:(void (^)(ObjectType obj, NSUInteger idx, BOOL *stop))block;
block為成功回調(diào),定義為:void (^)(ObjectType obj, NSUInteger idx, BOOL *stop);
obj:改參數(shù)由遍歷他的集合決定,如果是PHAssetCollection 的 api 獲得.那么他就是PHAssetCollection的對象,包括每組相冊的一些信息;
如果是PHAsset api 獲得,那他就是 PHAsset 對象,包含具體的每張圖片的信息.
idx:遍歷時每組相冊對應在集合中的下標.
stop:用于在某時刻停止遍歷資源.
- PHAsset:包含具體的每個照片的資源信息. 看看些常用的 api.
(1).資源的原信息.
mediaType:PHAssetMediaType類型的枚舉值:
PHAssetMediaTypeUnknown 不知類型
PHAssetMediaTypeImage 圖片
PHAssetMediaTypeVideo 視屏
PHAssetMediaTypeAudio 音頻
(2).資源的子類型.
mediaSubtypes:PHAssetMediaSubtype類型的枚舉值:
PHAssetMediaSubtypeNone 沒有任何子類型
相片子類型
PHAssetMediaSubtypePhotoPanorama 全景圖
PHAssetMediaSubtypePhotoHDR 濾鏡圖
PHAssetMediaSubtypePhotoScreenshot 截屏圖
PHAssetMediaSubtypePhotoLive 1.5s 的 photoLive
視屏子類型
PHAssetMediaSubtypeVideoStreamed 流體
PHAssetMediaSubtypeVideoHighFrameRate 高幀視屏
PHAssetMediaSubtypeVideoTimelapse 延時拍攝視頻
(3).獲得 PHAsset 的集合.
+ (PHFetchResult<PHAsset *> *)fetchAssetsWithOptions:(nullable PHFetchOptions *)options;
- PHImageManager:管理 PHAsset 的一個類,相當于對一個具體資源更好地管理和篩選.看寫常用的 api.
(1).獲得該實例.
+ (PHImageManager *)defaultManager;
(2).經(jīng)刪選和限制條件獲得具體的資源UIImage.
- (PHImageRequestID)requestImageForAsset:(PHAsset *)asset
targetSize:(CGSize)targetSize
contentMode:(PHImageContentMode)contentMode
options:(nullable PHImageRequestOptions *)options resultHandler:(void (^)(UIImage *__nullable result, NSDictionary *__nullable info))resultHandler;
返回值: PHImageRequestID,是個常量,定義為:static const PHImageRequestID PHInvalidImageRequestID = 0;
asset:想要獲得信息的PHAsset的對象,
targetSize:獲得圖片的尺寸大小,這里的大小是pixel,所以換算乘以[UIScreen mainScreen].scale.獲得自己想要的尺寸.
如果想要原圖的尺寸,直接傳入PHImageManagerMaximumSize.很大很大的尺寸,系統(tǒng)會默認返回原圖的尺寸,要注意的是傳入PHImageManagerMaximumSize時,則 contentMode 無論傳入什么值都會被視為PHImageContentModeDefault.
contentMode:想要圖片的裁剪方式, PHImageContentMode的枚舉:
PHImageContentModeAspectFit 適合的
PHImageContentModeAspectFill 鋪滿的
PHImageContentModeDefault = PHImageContentModeAspectFit
options: PHImageRequestOptions的實例,包括控制圖片版本,質量,裁剪參數(shù)等的一個類.
resultHandler:成功回調(diào)block,
result:獲取到的具體圖片,
info:關于圖片的一些信息,如是否來自 cloud, 是否是原圖等.
- PHCachingImageManager: PHImageManager的子類,讀獲取圖片的過程做緩存和清理的一個類.看看一些常用的 api.
(1).緩存操作.
- (void)startCachingImagesForAssets:(NSArray<PHAsset *> *)assets
targetSize:(CGSize)targetSize
contentMode:(PHImageContentMode)contentMode
options:(nullable PHImageRequestOptions *)options;
assets:要緩存獲取 PHAsset 類型對象的集合.
targetSize:獲取時的尺寸.
contentMode:裁剪方法,
options:傳入的控制參數(shù)類.
(2).取消緩存操作.
- (void)stopCachingImagesForAssets:(NSArray<PHAsset *> *)assets targetSize:(CGSize)targetSize contentMode:(PHImageContentMode)contentMode options:(nullable PHImageRequestOptions *)options;
assets:要緩存獲取 PHAsset 類型對象的集合.
targetSize:獲取時的尺寸.
contentMode:裁剪方法,
options:傳入的控制參數(shù)類.
- PHImageRequestOptions:控制加載圖片參數(shù)的一個類.看些常用的 api.
(1).控制圖片質量和獲取速度的 api
deliveryMode.
PHImageRequestOptionsDeliveryMode類型的枚舉,只有synchronous屬性設置為 YES,即異步獲取有限
PHImageRequestOptionsDeliveryModeOpportunistic 圖片質量和獲取速度均衡
PHImageRequestOptionsDeliveryModeHighQualityFormat 獲取高質量圖片,不保證獲取速度
PHImageRequestOptionsDeliveryModeFastFormat 快速獲得,不保證質量
(2).裁剪的方式
resizeMode.
PHImageRequestOptionsResizeMode類型的枚舉:
PHImageRequestOptionsResizeModeNone 不設置 PHImageRequestOptionsResizeModeFast 返回的圖像可能和目標大小不一樣并且質量較低,但效率高.
PHImageRequestOptionsResizeModeExact 返回圖像必須和目標大小相匹配,并且圖像質量也為高質量圖像
- PHPhotoLibrary:相冊權限管理,監(jiān)聽相冊增刪等.看看常用 api
(1).獲得實例
+ (PHPhotoLibrary *)sharedPhotoLibrary;
(2).獲得當前用戶授權情況
+ (PHAuthorizationStatus)authorizationStatus;
返回一個PHAuthorizationStatus類型的枚舉:
PHAuthorizationStatusNotDetermined 用戶還未選擇
PHAuthorizationStatusRestricted 家長模式 不允許
PHAuthorizationStatusDenied 不同意訪問
PHAuthorizationStatusAuthorized 同意訪問
(3).在第一次獲取時,系統(tǒng)提示選擇完后的回調(diào)方法
+ (void)requestAuthorization:(void(^)(PHAuthorizationStatus status))handler;
handler:該 block 就是用戶授權選擇完后的回調(diào),可獲得用戶選擇的結果
四.項目
主要分為兩個大模塊.一起是獲取相冊中的圖片,另一個為對獲取的圖片進行想要的尺寸裁剪.
- 獲取相冊中的圖片,單獨創(chuàng)建一個類 WFPhotoAlbum,作為獲取系統(tǒng)相冊資源的 manager,在內(nèi)部定義三個數(shù)組,在外部聲明一個公開的方法用來外部調(diào)用:
//相片原圖
@property (nonatomic, strong) NSMutableArray *fullPhotos;
//縮略圖
@property (nonatomic, strong) NSMutableArray *thumbnails;
//相冊列表資源
@property (nonatomic, strong) NSMutableArray *albums
/**
* 獲取圖片方法
*
* @param success 成功回調(diào)(分組列表,原圖,縮略圖)
* @param failure 失敗回調(diào)(error)
*/
- (void)getPhotosSuccess:(void (^)(NSMutableArray *groupPhotos,
NSMutableArray *fullPhotos,
NSMutableArray *thumbnails))success
failure:(void (^)(NSError *error))failure;
在方法的實現(xiàn)內(nèi)部,將耗時的操作放在子線程,比如讀取相冊資源等,吧主要的額回調(diào)提示等放在主線程.我們分 iOS8以下和 iOS8以上,在 iOS8以下,用的AssetsLibrary框架,將相片組的名稱和數(shù)量記錄下來:
[_albums addObject:[NSString stringWithFormat:@"%@(%li)張",[group valueForProperty:ALAssetsGroupPropertyName],group.numberOfAssets]];
AssetsLibrary框架可以直接提供縮略圖,直接保存下縮略圖:
[_thumbnails addObject:[UIImage imageWithCGImage:CGImage]];
保存原圖,這個地方保存的時候要壓縮下,不然相片一多就會內(nèi)存警告,會 crash:
[_fullPhotos addObject:UIImageJPEGRepresentation([UIImage imageWithCGImage:imageReference], 0.4)];
下面是iOS8以上的,用 Photos 框架的將相冊的名稱記錄下來:
[_albums addObject:obj.localizedTitle];
應為 Photos 采取的機制不一樣,他是由條件直接獲取到對應的資源,所以只要遍歷得到資源,就會占用內(nèi)存.在圖片稍微一多就會 crash, 所以我們在這采用保存 PHAsset 對象的辦法來解決這個crash, 這也是蘋果官方所采用的解決辦法.
PHFetchResult *assetsFetchResults = [PHAsset fetchAssetsWithOptions:nil];
[assetsFetchResults enumerateObjectsUsingBlock:^(PHAsset *_Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[_thumbnails addObject:obj];
[_fullPhotos addObject:obj];
}];
這里要注意兩點:
- AssetsLibrary對象應該是個全局變量,而不是局部變量,如果在他的生命周期外,我們所得到的資源是空的,這和他遍歷獲取資源庫的機制有關.
- 在AssetsLibrary框架下,如果用戶第一次選擇了權限,他會根據(jù)用戶的權限,會自動再去判斷獲取請求資源或者不請求,這個再次請求和用戶點擊授權的操作是系統(tǒng)自動處理的.保證用戶受了權限,在還能去請求一次資源,正常獲得資源,而 Photos 框架下是要開發(fā)者自己去管理,所以就有個那個用戶授權完的一個回調(diào),開發(fā)者需自行判斷再去是否請求一次資源.
我們在得到回調(diào)的資源時,去對遍歷操作做一次緩存.
if ([self.thumbnails.firstObject isKindOfClass:[PHAsset class]]) {
// 在資源的集合
_imageManager = [[PHCachingImageManager alloc] init];
//緩存操作
[_imageManager startCachingImagesForAssets:self.thumbnails
targetSize:PHImageManagerMaximumSize
contentMode:PHImageContentModeAspectFill
options:nil];
}
然后再在賦值的時候拿出記錄的 PHAsset 對象,給相應的控件賦值.此處獲取到的縮略圖只要按照原圖比例得到的小圖,要是直接使用,會產(chǎn)生變形,所以我們還要講此圖進行裁剪,按寬高,或得圖片正中間的一個正方向的圖.
//賦值
if ([obj isKindOfClass:[PHAsset class]]){
PHImageRequestOptions *requestOptions = [[PHImageRequestOptions alloc] init];
//異步
requestOptions.synchronous = YES;
//速度和質量均衡//synchronous ture 時有效
requestOptions.deliveryMode = PHImageRequestOptionsDeliveryModeOpportunistic;
//盡快提供要求左右的尺寸圖
requestOptions.resizeMode = PHImageRequestOptionsResizeModeExact;
// 遍歷資源的集合,獲取其中的圖片
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[_imageManager requestImageForAsset:obj
targetSize:CGSizeMake(125 * [UIScreen mainScreen].scale,
125 * [UIScreen mainScreen].scale)
contentMode:PHImageContentModeDefault
options:requestOptions
resultHandler:^(UIImage *result, NSDictionary *info) {
dispatch_async(dispatch_get_main_queue(), ^{
cell.imageView.image = [self wf_thumbnailsCutfullPhoto:result];
});
}];
});
}
//裁剪圖片,此處裁剪為125*125大的圖,即為我們的縮略圖
- (UIImage *)wf_thumbnailsCutfullPhoto:(UIImage*)fullPhoto
{
CGSize newSize;
CGImageRef imageRef = nil;
if ((fullPhoto.size.width / fullPhoto.size.height) < 1) {
newSize.width = fullPhoto.size.width;
newSize.height = fullPhoto.size.width * 1;
imageRef = CGImageCreateWithImageInRect([fullPhoto CGImage], CGRectMake(0, fabs(fullPhoto.size.height - newSize.height) / 2, newSize.width, newSize.height));
} else {
newSize.height = fullPhoto.size.height;
newSize.width = fullPhoto.size.height * 1;
imageRef = CGImageCreateWithImageInRect([fullPhoto CGImage], CGRectMake(fabs(fullPhoto.size.width - newSize.width) / 2, 0, newSize.width, newSize.height));
}
return [UIImage imageWithCGImage:imageRef];
}
-
裁剪獲得想要的相片尺寸.我是用 CAShapeLayer 做了個裁剪框,這樣的話,就不會影響到下面圖片的縮放,平移的交互,且將 layer 一定要加載在最上層.而此處的要被操作的圖片則是圖片原圖,且該處承載圖片的容器 UIImageView 也要用圖片的比例去動態(tài)設置.
平移縮放給視圖加的手勢來完成的,并且限制了一些操作,保證平移時圖片一直在裁剪框中,縮放時圖片不能小于裁剪框.這里就不寫代碼了,有點多,稍后附上 demo 地址,有興趣的小伙伴可以下載看看.效果圖:
最后就是選擇后確認裁剪,其實很簡單,就是將在整個大 view 的圖上的裁剪框內(nèi)的區(qū)域生成圖片,則這張圖就是咋們需要用到的圖片.然后經(jīng)過回調(diào)到咋們需要的地方去.
- (UIImage *)private_captureImageFromView:(UIView *)view{
CGRect screenRect = CGRectMake((self.view.frame.size.width - rectWidth) / 2,
(self.view.frame.size.height - rectHeight) / 2,
rectWidth,
rectHeight);
UIGraphicsBeginImageContextWithOptions(screenRect.size, NO, [UIScreen mainScreen].scale);
CGContextRef context = UIGraphicsGetCurrentContext();
if (context == NULL){
return nil;
}
//copy 一份圖形上下位文,用來操作
CGContextSaveGState(context);
//將當前圖行位文進行矩陣變換
CGContextTranslateCTM(context, -screenRect.origin.x, -screenRect.origin.y);
if( [view respondsToSelector:@selector(drawViewHierarchyInRect:afterScreenUpdates:)]){
//捕捉當前快照
[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:NO];
}else{
//layer 層渲染
[view.layer renderInContext:context];
}
//圖形位文退出棧
CGContextRestoreGState(context);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
最后這里附上demo 的地址,有興趣的小伙伴可以下載看看.! Demo.
修注:
在后面該項目中此模塊時,發(fā)現(xiàn)存在內(nèi)存問題.當圖片很多,上千上萬張,或者不停地一直滑動,就會出現(xiàn) ReceiveMemoryWarning,直至程序 crash.
出現(xiàn)這個問題后,在網(wǎng)上調(diào)研了一些資料,終于定位到問題的所在.所出現(xiàn)的內(nèi)存問題有兩個:
- 獲取完圖片后管理在圖片數(shù)量龐大時,稍顯乏力.
解決方法:重新做了圖片獲取后的管理.引入了兩個類. WFAlumbModel 和 WFCacheModel ,用于管理和交接在圖片和控制器之間的工作.獲取的方法改動為:
/**
獲取相冊的方法
@param success 成功獲取到模型 model
*/
- (void)getCameraSuccess:(void (^)(WFCacheModel *))success;
此方法內(nèi)部開辟子線程,先調(diào)用 WFCacheModel 的實例方法,將 PHFetchResult/ALAssetsLibrary 對象保存在 model 的 result 屬性中.
- (WFCacheModel *)modelWithResult:(id)result{
WFCacheModel *model = [[WFCacheModel alloc] init];
model.result = result;
return model;
}
然后重寫 result 的 set 方法,在 set 方法中調(diào)用 WFPhotoAlbum 的方法.
- (void)getAssetsFromFetchResult:(id)result
completion:(void (^)(NSArray<WFAlumbModel *> *))completion;
在此方法中方法,創(chuàng)建 WFAlumbModel 對象,將 PHAsset/ALAsset 對象保存在 WFAlumbModel 的 asset 屬性中.然后添加在數(shù)組中以 block 的方式返回賦值給 WFCacheModel 的 models 屬性,然后在獲取相冊的方法中. success 成功回調(diào)到 屬性 models 數(shù)組裝有 asset 的 WFAlumbModel 的 WFCacheModel 的集合.這樣的對所有的相關對象層級管理.分開管理.
在控制器回調(diào)到數(shù)據(jù)源.然后給 cell 賦值的時候.在 cell 中調(diào)用方法,返回 UIImage, 賦值就可以了.
- (PHImageRequestID)getPhotosWithAsset:(id)asset
originalImage:(BOOL)originalImage
completion:(void (^)(UIImage *))completion
- 在獲取到圖片的時候,我們要對圖片一些裁剪.在裁剪的方法中,只創(chuàng)建了 CGImageRef 對象,用完了后而沒有釋放,所以造成內(nèi)存泄露.從而引起 ReceiveMemoryWarning.
解決辦法:用完后釋放該對象.
CGImageRelease(imageRef);
做完這些優(yōu)化.現(xiàn)在內(nèi)存問題已經(jīng)不復存在了,性能也挺不錯的.前面對相冊管理的優(yōu)化由于代碼篇幅,沒有詳細的說明.有興趣的小伙伴可以下載 demo 看看,地址還是前面的那個,沒有變哦!