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
相关文章推荐
- iOS开发 关于ALAsset获取图库图片分辨率iOS7/iOS9适配
- ALAsset,ALAssetsLibrary,ALAssetsgroup常见属性及用法
- IOS将ALAsset对象中数据保存到本地文件夹
- 【iOS开发】---- ALAsset,ALAssetsLibrary,ALAssetsgroup常见属性及用法
- ALAsset 的用法(时间、url 、图片的获取)
- iOS 开发之照片框架详解PhotoKit,ALAssetsLibrary
- QBImagePickerController 下ALAsset ,PHAsset转图片的使用
- swift PhotoKit学习——swift(学习八)
- iOS 图片压缩
- 20160722关于保存应用图片到本地的相册
- PHAsset获取相册资源
- 使用ALAssetsLibrary读取所有照片
- 使用PHAsset获取相册中特定的图片
- Swift将PHAsset转为UIImage
- 保存图片到【相机胶卷】和【自定义相册】
- iOS8之后, 保存图片到本地相册, 避免重复保存同一张
- [HAXE] haxe接入IOS版FacebookSdK以及iOS开发遇到的问题总结(一)
- Core Animation - 核心动画
- IOS 消息推送原理及实现总结
- CALayer 图层简介