您的位置:首页 > 移动开发 > IOS开发

iOS7、8、9相册适配

2016-07-03 17:30 441 查看

前言

由于在iOS8及以后苹果将原有的操作相册的ALAssetsLibrary framework替换为Photos framework,所以,如果在应用中使用到的相册需要支持iOS8以下的系统版本的话,就需要了解Photos framework以做不同的版本适配。

一、iOS8以下

1. 几个重要的实体概念

ALAsset(iOS8及以后使用PHAsset)

一个
ALAsset
实例对象代表一个资源实体,比如一张图片、一个视频。共有三种类型:

ALAssetTypePhoto // 图片
ALAssetTypeVideo // 视频
ALAssetTypeUnknown // 未知


通过这个实例,你可以获取到这个资源的创建时间、资源类型、缩略图、二进制数据等信息。

ALAssetsGroup(iOS8及以后使用PHAssetCollection)

一个
ALAssetsGroup
实例对象代表一组资源的集合,也就是一个相册,可以是系统默认存在的相册(相机胶卷),也可以是开发者给用户创建的自定义相册(QQ)。

通过它,你可以获取到这个相册的名称、封面缩略图等。

ALAssetsLibrary(iOS8及以后使用PHPhotoLibrary)

一个
ALAssetsLibrary
实例对象对所有的相册资源进行索引。

使用它,你可以获取指定类型的相册、单张图片等对象,也可以添加新的自定义相册或者图片到相薄。如果你想要知道有没有获取到系统相薄的访问权限,同样需要用到它。

2. 实体相关API

ALAssetsLibrary

在iOS7及以下系统中,如果你想要获取相册中的资源,或者对相册进行操作,那么你先要创建一个ALAssetsLibrary实例对象。

获取指定类型的相册

- (void)enumerateGroupsWithTypes:(ALAssetsGroupType)types usingBlock:(ALAssetsLibraryGroupsEnumerationResultsBlock)enumerationBlock failureBlock:(ALAssetsLibraryAccessFailureBlock)failureBlock


注意

由于这里的遍历都是异步的操作,所以ALAssetsLibrary的实例对象需要被一个静态变量或者成员变量引用来保证不被销毁,回调的Block才能保证被成功调用。下面同此处。

根据一个相册URL获取这个相册

- (void)groupForURL:(NSURL *)groupURL resultBlock:(ALAssetsLibraryGroupResultBlock)resultBlock failureBlock:(ALAssetsLibraryAccessFailureBlock)failureBlock


创建一个相册

- (void)addAssetsGroupAlbumWithName:(NSString *)name resultBlock:(ALAssetsLibraryGroupResultBlock)resultBlock failureBlock:(ALAssetsLibraryAccessFailureBlock)failureBlock


相册授权状态

+ (ALAuthorizationStatus)authorizationStatus


ALAssetsGroup

iOS7及以下系统的中每个相册都是一个ALAssetsGroup实例,你可以使用这个实例获取这个相册的相关信息。

获取相册信息

- (id)valueForProperty:(NSString *)property


参数property包含以下类型:

ALAssetsGroupPropertyName

ALAssetsGroupPropertyType

ALAssetsGroupPropertyPersistentID

ALAssetsGroupPropertyURL

封面图

- (CGImageRef)posterImage


相册包含实体(照片、视频)的数量

- (NSInteger)numberOfAssets


过滤规则

- (void)setAssetsFilter:(ALAssetsFilter *)filter


注意

参数中的filter是一个ALAssetsFilter实例,这个实例只有三种类型:

+ (ALAssetsFilter *)allPhotos; // 所有图片

+ (ALAssetsFilter *)allVideos; // 所有视频

+ (ALAssetsFilter *)allAssets; // 所有视频及图片


在使用
- (NSInteger)numberOfAssets
获取相册实体数量时,会依赖该过滤规则。比如一个相薄中存在5个视频和5张图片,如果指定allPhotos,则numberOfAssets返回值为5。同样使用
- (void)enumerateAssetsUsingBlock:(ALAssetsGroupEnumerationResultsBlock)enumerationBlock
获取相册中的所有资源时,也只能获取到指定过滤规则下的资源。

使用相应遍历规则获取相册中的所有ALAsset资源

- (void)enumerateAssetsWithOptions:(NSEnumerationOptions)options usingBlock:(ALAssetsGroupEnumerationResultsBlock)enumerationBlock


3. 使用示例

获取所有相册,并过滤相册中的视频

+ (void)getAssetsGroupsForIos8BelowSuccess:(void (^)(NSMutableArray *))success failure:(void (^)(NSError *error))failure {
NSMutableArray *assetsGroups = [NSMutableArray array];
[[self library] enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
if (group != nil) {
[group setAssetsFilter:[ALAssetsFilter allPhotos]];
if (group.numberOfAssets > 0) {
[assetsGroups addObject:group];
}
} else {
if (success) {
success(assetsGroups);
}
}
} failureBlock:^(NSError *error) {
if (failure) {
failure(error);
}
}];
}
+ (ALAssetsLibrary *)library {
static ALAssetsLibrary *library;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
library = [[ALAssetsLibrary alloc] init];
});
return library;
}


获取一个相册中的所有资源

+ (void)getTimeLineSectionModelsForIos8BelowWithGroup:(MRAlbumGroupModel *)group success:(void (^)(NSMutableArray *))success failure:(void (^)(NSError *))failure {
NSMutableArray *sectionModels = [NSMutableArray array];
[group.assetGroup enumerateAssetsWithOptions:NSEnumerationReverse usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
if(result) {
[sectionModels addObject:result];
}
}];
if (success != nil && sectionModels.count > 0) {
success(sectionModels);
}
if (failure != nil && sectionModels.count == 0) {
failure(nil);
}
}


二、iOS8及以上

1. 几个重要的实体概念

与ALAssetsLibrary framework对应的几个实体

PHAsset

一个
PHAsset
实例对象与
ALAsset
类似,代表一个资源实体,比如一张图片、一个视频。与
ALAsset
不同之处在于,多了一种实体类型,共四种类型:

PHAssetMediaTypeUnknown = 0,
PHAssetMediaTypeImage   = 1,
PHAssetMediaTypeVideo   = 2,
PHAssetMediaTypeAudio   = 3,


同时,还多了更具体的子类型PHAssetMediaSubtype:

PHAssetMediaSubtypeNone               = 0,

// Photo subtypes
PHAssetMediaSubtypePhotoPanorama      = (1UL << 0),
PHAssetMediaSubtypePhotoHDR           = (1UL << 1),
PHAssetMediaSubtypePhotoScreenshot PHOTOS_AVAILABLE_IOS_TVOS(9_0, 10_0) = (1UL << 2),
PHAssetMediaSubtypePhotoLive PHOTOS_AVAILABLE_IOS_TVOS(9_1, 10_0) = (1UL << 3),

// Video subtypes
PHAssetMediaSubtypeVideoStreamed      = (1UL << 16),
PHAssetMediaSubtypeVideoHighFrameRate = (1UL << 17),
PHAssetMediaSubtypeVideoTimelapse     = (1UL << 18),


通过这个实例,除了可以获取到这个资源的创建时间、资源类型、缩略图、二进制数据等信息,还可以获取到location等位置信息。

PHCollection

PHCollection
是个基类,它有两个子类。分别是
PHAssetCollection
PHCollectionList
PHAssetCollection
代表 Photos 中的相册,
PHCollectionList
代表 Photos 中的文件夹。
PHCollectionList
里可嵌套
PHAssetCollection
,也可以嵌套自身类型,同时支持多重嵌套。

一个
PHAssetCollection
实例对象与
ALAssetsGroup
类似,代表一组资源的集合,也就是一个相册。

通过它,你可以获取到这个相册的名称、封面缩略图。

以及相册类型
PHAssetCollectionType


PHAssetCollectionTypeAlbum      = 1, // 自定义相册,如QQ
PHAssetCollectionTypeSmartAlbum = 2, // 相机胶卷、我的照片流、屏幕截图、全景照片等
PHAssetCollectionTypeMoment     = 3, // 时刻


相册子类型
PHAssetCollectionSubtype


// PHAssetCollectionTypeAlbum regular subtypes
PHAssetCollectionSubtypeAlbumRegular         = 2,
PHAssetCollectionSubtypeAlbumSyncedEvent     = 3,
PHAssetCollectionSubtypeAlbumSyncedFaces     = 4,
PHAssetCollectionSubtypeAlbumSyncedAlbum     = 5,
PHAssetCollectionSubtypeAlbumImported        = 6,

// PHAssetCollectionTypeAlbum shared subtypes
PHAssetCollectionSubtypeAlbumMyPhotoStream   = 100,
PHAssetCollectionSubtypeAlbumCloudShared     = 101,

// PHAssetCollectionTypeSmartAlbum subtypes
PHAssetCollectionSubtypeSmartAlbumGeneric    = 200,
PHAssetCollectionSubtypeSmartAlbumPanoramas  = 201,
PHAssetCollectionSubtypeSmartAlbumVideos     = 202,
PHAssetCollectionSubtypeSmartAlbumFavorites  = 203,
PHAssetCollectionSubtypeSmartAlbumTimelapses = 204,
PHAssetCollectionSubtypeSmartAlbumAllHidden  = 205,
PHAssetCollectionSubtypeSmartAlbumRecentlyAdded = 206,
PHAssetCollectionSubtypeSmartAlbumBursts     = 207,
PHAssetCollectionSubtypeSmartAlbumSlomoVideos = 208,
PHAssetCollectionSubtypeSmartAlbumUserLibrary = 209,
PHAssetCollectionSubtypeSmartAlbumSelfPortraits PHOTOS_AVAILABLE_IOS_TVOS(9_0, 10_0) = 210,
PHAssetCollectionSubtypeSmartAlbumScreenshots PHOTOS_AVAILABLE_IOS_TVOS(9_0, 10_0) = 211,

// Used for fetching, if you don't care about the exact subtype
PHAssetCollectionSubtypeAny = NSIntegerMax


PHPhotoLibrary

PHPhotoLibrary
是个单例对象,与
ALAssetsLibrary
相比发生了较大变化,只能申请获取PHOtos权限以及授权状态获取等。

使用这个单例你也可以注册相册改动的监听者,在相册发生变化(比如添加或导入了新图片)时,做一些刷新或其他处理。

新的实体

在iOS8及以上的相册资源获取中,都是使用
fetch
相关API的形式,过程类似
Core Data
。匹配资源的过程自然需要匹配规则,最终匹配结果也需要一个集合来记录。

PHFetchOptions

PHFetchOptions
的实例对象代表获取资源时的匹配规则。

可以指定资源排序规则(
NSArray<NSSortDescriptor *> *sortDescriptors;
)、资源类型规则(
NSPredicate *predicate
)、最大数量(
NSUInteger fetchLimit
)等。

PHFetchResult

PHFetchResult
的实例对象代表相册资源的匹配结果集合。它包含零个或多个符合匹配规则的资源,这些资源可以是
PHAssetCollection
对象,也可以是
PHAsset
对象。

PHImageManager

PHImageManager
是一个单例对象,不需要你手动创建,这个单例对象可以让你获取到一个PHAsset资源的实际二进制数据——如一张图片数据。

2. 实体相关API

PHAssetCollection

在iOS8及以上系统中,获取相册不需要创建实例,直接使用
PHAssetCollection
的类方法进行操作即可。

获取指定类型的相册

// Fetch asset collections of a single type and subtype provided (use PHAssetCollectionSubtypeAny to match all subtypes)
+ (PHFetchResult<PHAssetCollection *> *)fetchAssetCollectionsWithType:(PHAssetCollectionType)type subtype:(PHAssetCollectionSubtype)subtype options:(nullable PHFetchOptions *)options;


基本属性

@property (nonatomic, strong, readonly, nullable) NSString *localizedTitle; // 相册标题
@property (nonatomic, assign, readonly) PHAssetCollectionType assetCollectionType; // 相册类型
@property (nonatomic, assign, readonly) PHAssetCollectionSubtype assetCollectionSubtype; // 子类型
@property (nonatomic, assign, readonly) NSUInteger estimatedAssetCount; // 预估资源数量
@property (nonatomic, strong, readonly, nullable) NSDate *startDate; // 开始日期
@property (nonatomic, strong, readonly, nullable) NSDate *endDate; // 结束日期
@property (nonatomic, strong, readonly, nullable) CLLocation *approximateLocation; // 位置信息
@property (nonatomic, strong, readonly) NSArray<NSString *> *localizedLocationNames; // 位置名称


注意

estimatedAssetCount
并不是一个准确的值,当
PHAssetCollection
对象不能马上计算出当前相册中的资源数量时,会返回
NSNotFound
,如果想要获取资源的具体数量,需要使用
PHAsset
来fetch所有资源,计算资源数量。

PHAsset

获取某相册中的PHAsset资源

+ (PHFetchResult<PHAsset *> *)fetchAssetsInAssetCollection:(PHAssetCollection *)assetCollection options:(nullable PHFetchOptions *)options;


获取某相册的封面资源

+ (nullable PHFetchResult<PHAsset *> *)fetchKeyAssetsInAssetCollection:(PHAssetCollection *)assetCollection options:(nullable PHFetchOptions *)options;


注意

这个API可以获取到系统默认的一些封面图资源,这个结果由零个或多个PHAsset组成,在相册是smartAlbum类型的情况下,可能会有零个结果的情况。

获取中Photos中的所有PHAsset资源

+ (PHFetchResult<PHAsset *> *)fetchAssetsWithOptions:(nullable PHFetchOptions *)options;


注意

这里并不是获取某个相册中的PHAsset资源,而是手机中所有的PHAsset资源。

比如手机中有10个相册,每个相册中10个PHAsset资源,如果不指定PHFetchOptions,使用该方法会获取到所有的100个PHAsset资源。

获取Photos中指定资源类型的所有PHAsset资源

+ (PHFetchResult<PHAsset *> *)fetchAssetsWithMediaType:(PHAssetMediaType)mediaType options:(nullable PHFetchOptions *)options;


参数中的mediaType可选:

PHAssetMediaTypeUnknown = 0,
PHAssetMediaTypeImage   = 1,
PHAssetMediaTypeVideo   = 2,
PHAssetMediaTypeAudio   = 3,


PHImageManager

获取PHAsset图片资源的图片数据

- (PHImageRequestID)requestImageForAsset:(PHAsset *)asset targetSize:(CGSize)targetSize contentMode:(PHImageContentMode)contentMode options:(nullable PHImageRequestOptions *)options resultHandler:(void (^)(UIImage *__nullable result, NSDictionary *__nullable info))resultHandler;


注意

参数中的
PHImageRequestOptions
对象可以设置目标图片的一些属性,其中synchronous表示是否同步获取,默认是NO,也就是异步获取。

3. 使用示例

获取指定类型的相册

+ (NSMutableArray *)getCollecionsWithSmartAlbumSubtype:(PHAssetCollectionSubtype)subtype {
PHFetchOptions *userAlbumsOptions = [PHFetchOptions new];
PHFetchResult *userAlbumsResult = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum
subtype:PHAssetCollectionSubtypeAny
options:userAlbumsOptions];
PHFetchResult *userSmartAlbumsResult = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum
subtype:subtype
options:userAlbumsOptions];
NSMutableArray *collections = [NSMutableArray array];
void (^AlbumEnumerateObjectsUsingBlock)(PHAssetCollection *, NSUInteger idx, BOOL *) = ^(PHAssetCollection * _Nonnull collection, NSUInteger idx, BOOL * _Nonnull stop) {
if (collection.estimatedAssetCount == 0) {
return ;
}
NSUInteger numberOfAssets = 0;
PHFetchResult *assetsResult = [PHAsset fetchAssetsInAssetCollection:collection options:nil];
numberOfAssets = [assetsResult countOfAssetsWithMediaType:PHAssetMediaTypeImage];
if (numberOfAssets == 0) {
return;
}
[collections addObject:collection];
};
[userSmartAlbumsResult enumerateObjectsUsingBlock:AlbumEnumerateObjectsUsingBlock];
[userAlbumsResult enumerateObjectsUsingBlock:AlbumEnumerateObjectsUsingBlock];
return collections;
}


获取一个相册中的所有图片资源,按创建时间降序排序

+ (void)getTimeLineSectionModelsForIos8AboveWithGroup:(MRAlbumGroupModel *)group success:(void (^)(NSMutableArray *))success failure:(void (^)(NSError *))failure {
PHFetchResult *assetsResult = [PHAsset fetchAssetsInAssetCollection:group.collection options:[self fetchOptions]];
NSMutableArray *sectionModels = [NSMutableArray array];
[assetsResult enumerateObjectsUsingBlock:^(PHAsset * _Nonnull asset, NSUInteger idx, BOOL * _Nonnull stop) {
[sectionModels addObject:asset];
}];
if (success != nil && sectionModels.count > 0) {
success(sectionModels);
}
if (failure != nil && sectionModels.count == 0) {
failure(nil);
}
}
+ (PHFetchOptions *)fetchOptions {
PHFetchOptions *fetchOptions = [PHFetchOptions new];
fetchOptions.predicate = [NSPredicate predicateWithFormat:@"mediaType = %@", @(PHAssetMediaTypeImage)];
fetchOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:NO]];
return fetchOptions;
}


三、适配参考

我在不同系统的相册适配中采用适配器的方式进行的适配,仅供参考。

1. 适配器

相册适配主要是为了解决在iOS8以上的系统中,使用ALLibrary相关API可能存在一些不兼容的问题,导致资源获取不一致情况的出现。

在业务展现上并不期望因系统不同而给用户差别较大的体验,所以可以只针对相册资源数据获取的数据测适配即可,而无需改动UI。

这种场景可以在数据层对
ALAssetsGroup
PHAssetCollection
以及
ALAsset
PHAsset
做抽象,抽取适配器即可。

2. 简单示例

AssetGroupModel

@interface AlbumGroupModel : NSObject

@property (nonatomic, strong) UIImage *posterImage;
@property (nonatomic, assign) NSUInteger numberOfAssets;
@property (nonatomic, copy) NSString *assetsGroupPropertyName;

@property (nonatomic, strong) PHAssetCollection *collection;
@property (nonatomic, strong) ALAssetsGroup *assetGroup;

@end
@implementation AlbumGroupModel
- (UIImage *)posterImage {
if (_posterImage == nil) {
if ([XSLAppInfoTool systemVersion] < 8.0) {
_posterImage = [UIImage imageWithCGImage:self.assetGroup.posterImage];
} else {
PHFetchResult *groupResult = [PHAsset fetchAssetsInAssetCollection:self.collection options:self.class.fetchOptions];
PHAsset *asset = groupResult.firstObject;
PHImageRequestOptions *requestOptions = [[PHImageRequestOptions alloc] init];
requestOptions.resizeMode = PHImageRequestOptionsResizeModeExact;

CGFloat scale = [UIScreen mainScreen].scale;
CGFloat dimension = 80.0f;
CGSize size = CGSizeMake(dimension * scale, dimension * scale);
__block UIImage *resultImage = nil;
[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:size contentMode:PHImageContentModeAspectFill options:requestOptions resultHandler:^(UIImage *result, NSDictionary *info) {
resultImage = result;
}];
_posterImage = resultImage;
}
}
return _posterImage;
}
@end


AssetModel

@interface AlbumAssetModel : NSObject

@property (nonatomic, strong, nullable) PHAsset *assetPh;
@property (nonatomic, strong, nullable) ALAsset *assetAl;

- (void)thumbnail:(nullable void(^)(UIImage * _Nullable thumbnail))resultCallback;

@end
@implementation AlbumAssetModel

- (void)thumbnail:(void (^)(UIImage *))resultCallback {
if (self.assetPh) {
PHImageManager *manager = [PHImageManager defaultManager];
[manager requestImageForAsset:self.assetPh targetSize:CGSizeMake(100, 100) contentMode:PHImageContentModeDefault options:nil resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
if (resultCallback) {
resultCallback(result);
}
}];
} else if (self.assetAl) {
if (resultCallback) {
resultCallback([UIImage imageWithCGImage:[self.assetAl thumbnail]]);
}
}
}

@end
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息