使用AssetsLibrary和PhotoKit做一個簡易的相片選擇器

jlllx 8年前發布 | 8K 次閱讀 iOS開發 移動開發

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都返回一張圖片,只不過前者返回的圖片的質量高于或等于請求的質量,而后者可能返回一張質量稍低的圖片
  • 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

 

 本文由用戶 jlllx 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!