使用AssetsLibrary和PhotoKit做一個簡易的相片選擇器
iOS8之后,蘋果推出了PhotoKit,讓開發者在處理相冊相關的業務時,可以更加得心應手。github上的開發者針對PhotoKit做了一層很優秀的封裝 CTAssetsPickerController ,如果只需要支持iOS8+,那么可定制程度非常高的 CTAssetsPickerController 是個不錯的選擇。
但是由于現有的業務還是需要支持iOS7,所以并不能完全舍棄使用 AssetsLibrary 的方式來訪問相冊。因此也就需要自己封裝一套兼容iOS7的相冊管理器。
統一asset以及collection
AssetsLibrary | PhotoKit |
---|---|
ALAssetsGroup | PHAssetCollection |
ALAsset | PHAsset |
TBVAsset | TBVCollection |
相片選擇器最終需要向外部提供統一的標識相片的結構。同樣,統一結構能讓相片選擇器更加邏輯優雅地實現內部邏輯。所以這里我聲明了兩個對應的類: TBVAsset 、 TBVCollection ,并提供一些最基本的功能。
@interface TBVAsset : NSObject
/**
* PHAsset or ALAsset
*/
@property (strong, nonatomic) NSObject *asset;
+ (instancetype)assetWithOriginAsset:(NSObject *)asset;
/** 本地標識 */
- (NSString *)assetLocalIdentifer;
/** 源照片尺寸 */
- (CGSize)assetPixelSize;
@end
@interface TBVCollection : NSObject
/**
* ALAssetsGroup or PHAssetCollection
*/
@property (strong, nonatomic) NSObject *collection;
+ (instancetype)collectionWithOriginCollection:(NSObject *)aCollection;
/** 相簿名 */
- (NSString *)collectionTitle;
/** 估算的相簿相片個數 */
- (NSInteger)collectionEstimatedAssetCount;
/** 精確的相簿相片個數 */
- (NSInteger)collectionAccurateAssetCountWithFetchOptions:(id)filterOptions;
- (NSInteger)collectionAccurateAssetCountWithMediaType:(TBVAssetsPickerMediaType)mediaType;
@end
有了這些最基本的功能,在實現相冊選擇器時,就可以方便地對資源進行操作了。
其實對于這部分的兼容處理,主要就是對兩個不同的庫進行封裝,使其呈現同樣的外觀,后續的幾步大體也是圍繞這個目標進行。
封裝manager
由于是兩個不同版本的庫,并且AssetsLibrary已經在iOS9時被棄用,使用時會產生 deprecated 警告,所以我分別對 ALAssetsLibrary 和 PHCachingImageManager 進行了封裝,然后通過統一的接口 TBVAssetsManagerProtocol 暴露其功能。
一般相冊選擇器具有如下頁面及對應功能(UI展示):
- 首頁
- 相簿名
- 相簿縮略圖
- 相簿擁有相片數
- 預覽頁
- 相片縮略圖
- 選中相片大小
- 瀏覽頁
- 相片大圖
- 選中相片大小
所以我提供的接口如下:
//====================================
// image
//====================================
/** requestImage返回都是一個RACTuple,first是Image,second是是否為degraded */
/** 請求特定大小的圖片 */
- (RACSignal *)requestImageForAsset:(TBVAsset *)asset
targetSize:(CGSize)targetSize
contentMode:(TBVAssetsPickerContentMode)contentMode;
/** 請求相簿縮略圖 */
- (RACSignal *)requestPosterImageForCollection:(TBVCollection *)collection
mediaType:(TBVAssetsPickerMediaType)mediaType;
/** 請求相片縮略圖 */
- (RACSignal *)requestPosterImageForAsset:(TBVAsset *)asset;
/** 請求相片原圖 */
- (RACSignal *)requestFullResolutionImageForAsset:(TBVAsset *)asset;
//====================================
// asset / collection
//====================================
/** 請求相片資源大小 */
- (RACSignal *)requestSizeForAssets:(NSArray <TBVAsset *> *)assets;
/** 請求所有相簿 */
- (RACSignal *)requestAllCollections;
/** 請求所有相片資源 */
- (RACSignal *)requestAssetsForCollection:(TBVCollection *)collection
mediaType:(TBVAssetsPickerMediaType)mediaType;
/** 請求相機膠卷相簿(針對一般業務首先進入相機膠卷的預覽頁) */
- (RACSignal *)requestCameraRollCollection;
//====================================
// video
//====================================
/** 請求AVPlayerItem */
- (RACSignal *)requestVideoForAsset:(TBVAsset *)asset;
/** 請求AVURLAsset */
- (RACSignal *)requestURLAssetForAsset:(TBVAsset *)asset;
實現以上接口,一般相冊選擇器的功能點就已經完成大半了。
TBVAssetsPickerManager
由于自定義的相冊manager都遵守 TBVAssetsManagerProtocol , TBVAssetsPickerManager
的實現就變得相對簡單,沒有一大串令人厭煩的 if-else 。當然 TBVAssetsPickerManager 本身也是遵守 TBVAssetsManagerProtocol 的。
@interface TBVAssetsPickerManager()
@property (strong, nonatomic) NSObject<TBVAssetsManagerProtocol> *realManager;
@property (strong, nonatomic) NSMutableArray *requestIdList;
@property (assign, nonatomic) BOOL photoKitAvailable;
@end
#pragma mark life cycle
- (instancetype)init {
self = [super init];
if (self) {
_photoKitAvailable = NSClassFromString(@"PHImageManager") != nil;
}
return self;
}
#pragma mark TBVAssetsManagerProtocol
....
#pragma mark getter setter
- (NSObject *)realManager
{
if (_realManager == nil) {
if (self.photoKitAvailable) {
_realManager = [[TBVCachingImageManager alloc] init];
} else {
_realManager = [[TBVAssetsLibrary alloc] init];
}
}
return _realManager;
}
這樣 _realManager 拿到的就是當前版本最新的相冊manager了。
接口的實現
其實接口文檔描述的還是非常清晰的,所以這里只是羅列了下代碼,并沒有針對每一步做解釋,因為這些基本的操作進去頭文件看看就全明白了。
- requestImageForAsset:targetSize:(CGSize)targetSize:contentMode:
這個接口主要用來獲取非原圖。
- TBVCachingImageManager
- 關于PHImageRequestOptions的deliveryMode,
- 設置為PHImageRequestOptionsDeliveryModeOpportunistic并且synchronous為NO時,請求可能會先返回一張縮略圖,然后再返回一張大圖,這個可以通過獲取請求回調字典中PHImageResultIsDegradedKey對應value來判別
- PHImageRequestOptionsDeliveryModeHighQualityFormat和PHImageRequestOptionsDeliveryModeFastFormat都返回一張圖片,只不過前者返回的圖片的質量高于或等于請求的質量,而后者可能返回一張質量稍低的圖片
- 關于PHImageRequestOptions的deliveryMode,
- TBVAssetsLibrary
- 由于AssetsLibrary并沒有提供獲取特定尺寸的相片接口,所以這里只是返回thumbnail、aspectRatioThumbnail、fullScreenImage中尺寸和目標大小最接近的一張圖片。
// TBVCachingImageManager
- (RACSignal *)requestImageForAsset:(TBVAsset *)asset
targetSize:(CGSize)targetSize
contentMode:(TBVAssetsPickerContentMode)contentMode {
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
PHImageRequestID requestId = [self.imageManager
requestImageForAsset:(PHAsset *)asset.asset
targetSize:targetSize
contentMode:[self contentModeForCustomContentMode:contentMode]
options:self.defaultImageRequestOptions
resultHandler:^(UIImage * _Nullable result,
NSDictionary * _Nullable info) {
[subscriber sendNext:RACTuplePack(result, info[PHImageResultIsDegradedKey])];
if (![info[PHImageResultIsDegradedKey] boolValue]) {
[subscriber sendCompleted];
}
}];
return [RACDisposable disposableWithBlock:^{
[self.imageManager cancelImageRequest:requestId];
}];
}];
}
// TBVAssetsLibrary
- (RACSignal *)requestImageForAsset:(TBVAsset *)aAsset
targetSize:(CGSize)targetSize
contentMode:(TBVAssetsPickerContentMode)contentMode {
return [[[RACSignal return:aAsset.asset]
deliverOn:[RACScheduler scheduler]]
map:^id(ALAsset * asset) {
CGImageRef resultImageRef = nil;
BOOL degraded = NO;
if (targetSize.width < CGImageGetWidth(asset.thumbnail) &&
targetSize.height < CGImageGetHeight(asset.thumbnail)) {
// TBVAssetsPickerContentModeFill
resultImageRef = asset.thumbnail;
degraded = YES;
}
if (!resultImageRef) {
CGImageRef aspectRatioThumbnail = asset.aspectRatioThumbnail;
if (targetSize.width < CGImageGetWidth(aspectRatioThumbnail) &&
targetSize.height < CGImageGetHeight(aspectRatioThumbnail)) {
// TBVAssetsPickerContentModeFit
resultImageRef = aspectRatioThumbnail;
}
if (!resultImageRef) {
ALAssetRepresentation *assetRepresentation = [asset defaultRepresentation];
resultImageRef = [assetRepresentation fullScreenImage];
}
}
UIImage *resultImage = nil;
if (resultImageRef) {
resultImage = [UIImage imageWithCGImage:resultImageRef
scale:BQAP_SCREEN_SCALE
orientation:UIImageOrientationUp];
}
return RACTuplePack(resultImage, @(degraded));
}];
}
-requestPosterImageForCollection:mediaType:
- TBVCachingImageManager
- 通過-fetchKeyAssetsInAssetCollection:options:獲取相簿keyAssets,最多可以返回三個
- 最終還是通過requestPosterImageForAsset:獲取縮略圖
- 獲取keyAssets前,需要設置options對資源進行過濾:
+ (instancetype)tbv_fetchOptionsWithCustomMediaType:(TBVAssetsPickerMediaType)mediaType {
NSArray *mediaTypes = [self tbv_mediaTypesWithCustonMediaType:mediaType];
if (!mediaTypes.count) return nil;
PHFetchOptions *options = [[PHFetchOptions alloc] init];
NSMutableString *predicateString = [NSMutableString string];
for (NSNumber *mediaType in mediaTypes) {
[predicateString appendFormat:@"mediaType = %@", mediaType];
if (![mediaType isEqual:mediaTypes.lastObject]) [predicateString appendString:@" || "];
}
options.predicate = [NSPredicate predicateWithFormat:predicateString];
return options;
}
- TBVAssetsLibrary
- ALAssetsGroup有posterImage屬性,直接返回相簿縮略圖
- 和PhotoKit不同,AssetLibrary通過ALAssetsGroup的setAssetsFilter方法進行過濾
[group setAssetsFilter:[ALAssetsFilter tbv_assetsFilterWithCustomMediaType:mediaType]];
這樣設置以后,后續針對group的操作都會在過濾的結果中進行了。
// TBVCachingImageManager
- (RACSignal *)requestPosterImageForCollection:(TBVCollection *)collection
mediaType:(TBVAssetsPickerMediaType)mediaType {
return [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
PHFetchOptions *fetchOptions = [PHFetchOptions tbv_fetchOptionsWithCustomMediaType:mediaType];
fetchOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate"
ascending:YES]];
PHAssetCollection *realCollection = (PHAssetCollection *)collection.collection;
/* fetchKeyAssetsInAssetCollection 獲取至多三張 */
PHFetchResult *result = [PHAsset fetchKeyAssetsInAssetCollection:realCollection
options:fetchOptions];
if (!result.count) {
[subscriber sendNext:[RACSignal empty]];
[subscriber sendCompleted];
return nil;
}
TBVAsset *posterAsset = [TBVAsset assetWithOriginAsset:result.firstObject];
[subscriber sendNext:[self requestPosterImageForAsset:posterAsset]];
[subscriber sendCompleted];
return nil;
}] switchToLatest];
}
// TBVAssetsLibrary
- (RACSignal *)requestAssetsForCollection:(TBVCollection *)collection
mediaType:(TBVAssetsPickerMediaType)mediaType {
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSMutableArray *assets = [NSMutableArray array];
ALAssetsGroup *group = (ALAssetsGroup *)collection.collection;
[group setAssetsFilter:[ALAssetsFilter tbv_assetsFilterWithCustomMediaType:mediaType]];
[group enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
if (result) {
[assets addObject:[TBVAsset assetWithOriginAsset:result]];
} else {
[subscriber sendNext:assets];
[subscriber sendCompleted];
}
}];
return nil;
}];
}
-requestPosterImageForAsset:
獲取asset的縮略圖,需要注意的一點就是:在獲取縮略圖的情況下,Fill比Fit獲取的圖片要清晰
// TBVCachingImageManager
- (RACSignal *)requestPosterImageForAsset:(TBVAsset *)asset {
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
CGSize posterSize = CGSizeMake(kBQPosterImageWidth * BQAP_SCREEN_SCALE,
kBQPosterImageHeight * BQAP_SCREEN_SCALE);
/* 在獲取縮略圖的情況下,Fill比Fit獲取的圖片要清晰 */
PHImageRequestID requestId = [self.imageManager
requestImageForAsset:(PHAsset *)asset.asset
targetSize:posterSize
contentMode:PHImageContentModeAspectFill
options:self.defaultImageRequestOptions
resultHandler:^(UIImage * _Nullable result,
NSDictionary * _Nullable info) {
[subscriber sendNext:RACTuplePack(result, info[PHImageResultIsDegradedKey])];
if (![info[PHImageResultIsDegradedKey] boolValue]) {
[subscriber sendCompleted];
}
}];
return [RACDisposable disposableWithBlock:^{
[self.imageManager cancelImageRequest:requestId];
}];
}];
}
// TBVAssetsLibrary
- (RACSignal *)requestPosterImageForAsset:(TBVAsset *)asset {
return [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
CGSize posterSize = CGSizeMake(kBQPosterImageWidth * BQAP_SCREEN_SCALE,
kBQPosterImageHeight * BQAP_SCREEN_SCALE);
[subscriber sendNext:[self requestImageForAsset:asset
targetSize:posterSize
contentMode:TBVAssetsPickerContentModeFill]];
[subscriber sendCompleted];
return nil;
}] switchToLatest];
}
-requestFullResolutionImageForAsset:
獲取原圖時有一點很重要,就是盡量不要快速連續地獲取原圖,大圖也可以列入這個范疇。連續地獲取大圖或者原圖,設備的內存會急劇增高,甚至崩潰,這種情況通常在上傳圖片時比較常見。
所以在上傳圖片時,盡量上傳一張原圖后再獲取下一張原圖進行上傳,而不是全部獲取完成之后再上傳。
// TBVCachingImageManager
- (RACSignal *)requestFullResolutionImageForAsset:(TBVAsset *)asset {
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
self.defaultImageRequestOptions.networkAccessAllowed = YES;
PHImageRequestID requestId = [self.imageManager
requestImageForAsset:(PHAsset *)asset.asset
targetSize:PHImageManagerMaximumSize
contentMode:PHImageContentModeDefault
options:self.defaultImageRequestOptions
resultHandler:^(UIImage * _Nullable result,
NSDictionary * _Nullable info) {
[subscriber sendNext:RACTuplePack(result, info[PHImageResultIsDegradedKey])];
if (![info[PHImageResultIsDegradedKey] boolValue]) {
[subscriber sendCompleted];
}
}];
return [RACDisposable disposableWithBlock:^{
[self.imageManager cancelImageRequest:requestId];
}];
}];
}
// TBVAssetsLibrary
- (RACSignal *)requestFullResolutionImageForAsset:(TBVAsset *)asset {
return [[[RACSignal return:asset.asset]
deliverOn:[RACScheduler scheduler]]
map:^id(ALAsset * asset) {
ALAssetRepresentation *assetRepresentation = [asset defaultRepresentation];
CGImageRef fullResolutionImage = [assetRepresentation fullResolutionImage];
UIImage *resultImage = [UIImage imageWithCGImage:fullResolutionImage
scale:BQAP_SCREEN_SCALE
orientation:UIImageOrientationUp];
return RACTuplePack(resultImage, @(NO));
}];
}
-requestSizeForAssets:
請求大小是針對的圖片,所以對非圖片的asset進行了過濾
// TBVCachingImageManager
- (RACSignal *)requestSizeForAssets:(NSArray<TBVAsset *> *)assets {
RACSequence *requestSequence = [[assets.rac_sequence
filter:^BOOL(TBVAsset *asset) {
return ((PHAsset *)asset.asset).mediaType == PHAssetMediaTypeImage;
}]
map:^id(TBVAsset *asset) {
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
self.defaultImageRequestOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
PHImageRequestID requestId =[self.imageManager
requestImageDataForAsset:(PHAsset *)asset.asset
options:self.defaultImageRequestOptions
resultHandler:^(NSData * _Nullable imageData,
NSString * _Nullable dataUTI,
UIImageOrientation orientation,
NSDictionary * _Nullable info) {
[subscriber sendNext:@(imageData.length)];
[subscriber sendCompleted];
}];
return [RACDisposable disposableWithBlock:^{
[self.imageManager cancelImageRequest:requestId];
}];
}];
}];
return [[RACSignal
zip:requestSequence]
map:^id(RACTuple *value) {
return [value.rac_sequence foldLeftWithStart:@0 reduce:^id(id accumulator, id value) {
return @([accumulator integerValue] + [value integerValue]);
}];
}];
}
// TBVAssetsLibrary
- (RACSignal *)requestSizeForAssets:(NSArray<TBVAsset *> *)assets {
return [RACSignal
return:[[[[assets.rac_sequence
map:^id(TBVAsset *asset) {
return asset.asset;
}]
filter:^BOOL(ALAsset *asset) {
return [asset valueForProperty:ALAssetPropertyType] == ALAssetTypePhoto;
}]
map:^id(ALAsset *asset) {
return @([asset defaultRepresentation].size);
}]
foldLeftWithStart:@(0) reduce:^id(id accumulator, id value) {
return @([accumulator integerValue] + [value integerValue]);
}]];
}
-requestAllCollections
// TBVCachingImageManager
- (RACSignal *)requestAllCollections {
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
PHFetchResult *smartResult = [PHAssetCollection
fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum
subtype:PHAssetCollectionSubtypeAlbumRegular
options:nil];
PHFetchResult *topLevelUserCollections = [PHCollectionList fetchTopLevelUserCollectionsWithOptions:nil];
NSMutableArray *collections = [NSMutableArray array];
for (PHAssetCollection *aCollection in smartResult) {
TBVCollection *collection = [TBVCollection collectionWithOriginCollection:aCollection];
[collections addObject:collection];
}
for (PHAssetCollection *aCollection in topLevelUserCollections) {
TBVCollection *collection = [TBVCollection collectionWithOriginCollection:aCollection];
[collections addObject:collection];
}
[subscriber sendNext:collections];
[subscriber sendCompleted];
return nil;
}];
}
// TBVAssetsLibrary
- (RACSignal *)requestAllCollections {
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSMutableArray *collections = [NSMutableArray array];
[self.assetsLibrary enumerateGroupsWithTypes:ALAssetsGroupAll
usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
if (group) {
TBVCollection *collection = [TBVCollection collectionWithOriginCollection:group];
[collections addObject:collection];
} else {
[subscriber sendNext:collections];
[subscriber sendCompleted];
}
} failureBlock:^(NSError *error) {
[subscriber sendError:error];
}];
return nil;
}];
}
-requestAssetsForCollection:mediaType:
// TBVCachingImageManager
- (RACSignal *)requestAssetsForCollection:(TBVCollection *)collection
mediaType:(TBVAssetsPickerMediaType)mediaType {
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
PHFetchOptions *options = [PHFetchOptions tbv_fetchOptionsWithCustomMediaType:mediaType];
PHFetchResult *fetchResult = [PHAsset fetchAssetsInAssetCollection:(PHAssetCollection *)collection.collection
options:options];
NSMutableArray *assets = [NSMutableArray arrayWithCapacity:fetchResult.count];
for (PHAsset *asset in fetchResult) {
[assets addObject:[TBVAsset assetWithOriginAsset:asset]];
}
[subscriber sendNext:assets];
[subscriber sendCompleted];
return nil;
}];
}
// TBVAssetsLibrary
- (RACSignal *)requestAssetsForCollection:(TBVCollection *)collection
mediaType:(TBVAssetsPickerMediaType)mediaType {
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSMutableArray *assets = [NSMutableArray array];
ALAssetsGroup *group = (ALAssetsGroup *)collection.collection;
[group setAssetsFilter:[ALAssetsFilter tbv_assetsFilterWithCustomMediaType:mediaType]];
[group enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
if (result) {
[assets addObject:[TBVAsset assetWithOriginAsset:result]];
} else {
[subscriber sendNext:assets];
[subscriber sendCompleted];
}
}];
return nil;
}];
}
-requestCameraRollCollection
// TBVCachingImageManager
- (RACSignal *)requestCameraRollCollection {
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
PHFetchResult *smartAlbums = [PHAssetCollection
fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum
subtype:PHAssetCollectionSubtypeSmartAlbumUserLibrary
options:nil];
[subscriber sendNext:[TBVCollection collectionWithOriginCollection:smartAlbums.firstObject]];
[subscriber sendCompleted];
return nil;
}];
}
// TBVAssetsLibrary
- (RACSignal *)requestCameraRollCollection {
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[self.assetsLibrary enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
[subscriber sendNext:[TBVCollection collectionWithOriginCollection:group]];
[subscriber sendCompleted];
} failureBlock:^(NSError *error) {
[subscriber sendError:error];
}];
return nil;
}];
}
-requestVideoForAsset:和-requestURLAssetForAsset:
由于業務上沒有視頻的需求,所以這一塊還沒有去實現。
實現過程中的一些小坑
用ALAssetsLibrary申請訪問相冊權限
這一點貌似有些代碼用authorizationStatus就能實現,不過
應用的時候還是發現不能觸發請求權限alert,所以這里需要訪問下相冊的資源 ask-permission-to-access-camera-roll ,
來觸發這個請求動作:
+ (RACSignal *)tbv_forceTriggerPermissionAsking {
return [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
void (^sendStatus)() = ^{
[subscriber sendNext:@([self tbv_authorizationStatus])];
[subscriber sendCompleted];
};
if ([self tbv_authorizationStatus] != BQAuthorizationStatusNotDetermined) {
sendStatus();
return nil;
}
ALAssetsLibrary *lib = [[ALAssetsLibrary alloc] init];
[lib enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
sendStatus();
*stop = YES;
} failureBlock:^(NSError *error) {
sendStatus();
}];
return nil;
}] deliverOnMainThread];
}
ALAssetsLibrary請求的ALAsset被置空問題
官方文檔里面有這么一句:
The lifetimes of objects you get back from a library instance are tied to the lifetime of the library instance.
可以看到,在使用ALAssetsLibrary請求回來的資源時,是不能釋放對應的ALAssetsLibrary對象的。在發送多圖的場合,如果不注意保持住ALAssetsLibrary對象,很容易發生后面幾張圖片獲取不到的情況。
所以,要么在選擇器返回選中的資源時,強引用對應的manager,要么這個manager由調用者傳給相冊選擇器。
更改應用權限并切回前臺
如果應用在后臺時,更改了權限,當切回前臺后,App會重新啟動 。這里如果設置了斷點,別以為是程序崩了。
不足
一個很明顯的問題是使用了RAC的版本后,相冊選擇器滾動的性能會下降,沒有以前通過回調來的順暢。如果稍微快速一點滾動的話,CPU很容易就上100%。
可能是使用RAC的方式不是很正確造成的,后續盡可能優化這一塊。
來自:http://www.jianshu.com/p/78a035ca3800