iOS 8+(PhotoKit) 获取相册资源工具(扩展性较强)

96
LiYaoPeng
0.1 2018.07.17 16:32 字数 575

1. 参考资料:

感谢!感谢!感谢! 重三~

  1. 学习请点下面链接:
    iOS 开发之照片框架详解之二 —— PhotoKit 详解(上)
    iOS 开发之照片框架详解之二 —— PhotoKit 详解(下)
  2. 获取相册资源的工具请下载:
    里面提供了数据的处理类,以及选取photo UI的封装。自定义扩展性强。
    去下载demo

2. 数据处理思路

  1. 每次都是从外向内分析,这次从底层向上分析
    获取相册 -> 获取相册中的资源 -> 获取资源中的(图片或者视频)
  2. 主要是对数据的处理,把UI分离出来,让你方便的获取相册资源,以及管理被选中的资源
  3. 预览图,宽度最大为 500像素。(建议400)
  4. 封装后,你所有的数据,都可以通过PYAblum 单例拿到
  5. 最后,需要在获取相册资源模块使用完成后,清空缓存 - (void)removeCache;

1. 获取相册

主要方法

PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
  1. 根据类型 获取相册:
    自定义一个枚举,实现自定义获取相册类型
///选取相册类型
typedef NS_ENUM(NSUInteger, Enum_PYAblumManager_AblumType){
    ///所有相册
    Enum_Ablum_AblumAll = 1 << 0,
    
    ///PHAssetCollectionSubtypeAlbumRegular         = 2, // 在 相册 应用中创建的相簿
    Enum_Ablum_smartAlbums = 1 << 2,
    
    ///获取所有用户创建的相册 PHAssetCollectionSubtypeAlbumRegular
    Enum_Ablum_topLevelUserCollections = 1 << 3,
    
    /// PHAssetCollectionSubtypeAlbumSyncedAlbum     = 5, // 从iPhone中同步到设备的相簿
    Enum_Ablum_syncedAlbums = 1 << 4,
    
    ///PHAssetCollectionSubtypeAlbumMyPhotoStream   = 100, // 用户自己的iCloud照片流
    Enum_Ablum_myPhotoStreamAlbum = 1 << 5,
    
    ///PHAssetCollectionSubtypeAlbumCloudShared     = 101, // 一个iCloud共享照片流
    Enum_Ablum_sharedAlbums = 1 << 6,
};
  1. 根据枚举来获取相册列表
- ( NSArray <PHFetchResult<PHAssetCollection *>*>*)getFetchResultAblumType: (Enum_PYAblumManager_AblumType)ablumType {
    NSMutableArray <PHFetchResult *>* fetchResultArrayM = [[NSMutableArray alloc]init];
    ablumType = [self ablumType:ablumType];
    // 用户自己的iCloud照片流
    if ((ablumType & Enum_Ablum_myPhotoStreamAlbum) == Enum_Ablum_myPhotoStreamAlbum) {
        PHFetchResult *myPhotoStreamAlbum = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumMyPhotoStream options:nil];
        [fetchResultArrayM addObject:myPhotoStreamAlbum];
    }
    
    // 在 相册 应用中创建的相簿
    if((ablumType & Enum_Ablum_smartAlbums) == Enum_Ablum_smartAlbums) {
        PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
        [fetchResultArrayM addObject:smartAlbums];
    }
    
    // 获取所有用户创建的相册
    if((ablumType & Enum_Ablum_topLevelUserCollections) == Enum_Ablum_topLevelUserCollections) {
        PHFetchResult *topLevelUserCollections = [PHCollectionList fetchTopLevelUserCollectionsWithOptions:nil];
        [fetchResultArrayM addObject:topLevelUserCollections];
    }

    /// 从iPhone中同步到设备的相簿
    if ((ablumType & Enum_Ablum_syncedAlbums) == Enum_Ablum_syncedAlbums) {
        PHFetchResult *syncedAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumSyncedAlbum options:nil];
        [fetchResultArrayM addObject:syncedAlbums];
    }
    ///
    if ((ablumType & Enum_Ablum_sharedAlbums) == Enum_Ablum_syncedAlbums) {
        PHFetchResult *sharedAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeAlbum subtype:PHAssetCollectionSubtypeAlbumCloudShared options:nil];
        [fetchResultArrayM addObject:sharedAlbums];
    }
    return fetchResultArrayM.copy;
}
  1. PHFetchResult<PHAssetCollection *>*变为相册model
    这里的model,类型不能写死,需要提供接口,才能得到最大扩展性。
    传入自定义model的class,不过,自定义model必须继承自PYAblumModel
/// 返回相册
- (PYAblumModel *)getFetchResultWithAssetType: (Enum_PYAblumManager_AssetType)assetType andAblumType: (Enum_PYAblumManager_AblumType) ablumType allBlum:(void(^)(NSArray <PYAblumModel *>* blumArray))allBlumBlock {

    NSSortDescriptor *sortDescriptor = nil;
    PHFetchOptions *options = [[PHFetchOptions alloc]init];
    ///是否按时间排序
    if (self.isTimeRank) {
        sortDescriptor = [[NSSortDescriptor alloc]initWithKey:@"creationDate" ascending:false];
        options.sortDescriptors = @[sortDescriptor];
    }
    /// handle assetType
    options = [self setOptions:options WithAssetType:assetType];
    ///获取 fetchResult
    NSArray <PHFetchResult *>* fetchResultArray = [self getFetchResultAblumType:ablumType];
    
    NSMutableArray <PYAblumModel *>* fetchResultArrayM = [NSMutableArray new];
    for (PHFetchResult *fetchResult in fetchResultArray) {
        for (PHAssetCollection *collection in fetchResult) {
            // 有可能是PHCollectionList类的的对象,过滤掉
            if (![collection isKindOfClass:[PHAssetCollection class]]) continue;
            PHFetchResult *fetchResult = [PHAsset fetchAssetsInAssetCollection:collection options:options];
            if (fetchResult.count < 1) continue;
            if ([collection.localizedTitle containsString:@"Deleted"] || [collection.localizedTitle isEqualToString:@"最近删除"]) continue;
           
            if (!self.ablumModelClass) {
                self.ablumModelClass = PYAblumModel.class;
                NSLog(@"🐷self.ablumClass, 没有设置值,自动设置为 PYAblumModel");
            }
            PYAblumModel *model = (PYAblumModel *)[self.ablumModelClass new];
            model.name = collection.localizedTitle;
            model.assetfetchResult = fetchResult;
            
            //设置封面
            
            if ([self isCameraRollAlbum:collection.localizedTitle]) {
                [fetchResultArrayM insertObject:model atIndex:0];
            } else {
                [fetchResultArrayM addObject:model];
            }
        }
    }
    NSArray *bulmArray = fetchResultArrayM.copy;
    if (allBlumBlock) allBlumBlock(bulmArray);
    return bulmArray.firstObject;
}
  1. 来看一下PYAblumManager.h
    这里提供了一些自定义接口
@interface PYAblumManager : NSObject

/// 是否按照时间对资源排序(默认为true)
@property (nonatomic,assign) BOOL isTimeRank;

/// 相册的 model class 一定要继承自(PYAblumManagerModel)
@property (nonatomic,strong) Class ablumModelClass;

/// 返回传入的modelClass对象 主相册, block里面为相册数组 
- (PYAblumModel *)getFetchResultWithAssetType: (Enum_PYAblumManager_AssetType)assetType andAblumType: (Enum_PYAblumManager_AblumType) ablumType allBlum:(void(^)(NSArray <PYAblumModel *>* blumArray))allBlumBlock;
@end
  1. PYAblumModel.h
    在model中你可以拿到 封面 + name + 相册的资源集合
@interface PYAblumModel : NSObject
/// 封面
@property (nonatomic,strong) UIImage *coverImageView;
@property (nonatomic,copy) NSString *name;
@property (nonatomic,strong) PHFetchResult<PHAsset *> *assetfetchResult;
@end

2. 获取资源

  1. 主要是 对资源进行筛选,大小,时长等限制,并生成PYAssetModel数组
  2. 对选择的资源进行了处理,记录了选中状态以及选中顺序。
  3. 对model 的自定义。一定要继承自PYAssetModel
///资源管理者, 获取 相册中的资源
@interface PYAssetManager : NSObject

//MARK: - 筛选
///最小的 图片的size 默认 为 (0,0)
@property (nonatomic,assign) CGSize minSize;
///最大的 图片的size 默认 为(80,80)
@property (nonatomic,assign) CGSize maxSize;
///最短时长 默认为 0
@property (nonatomic,assign) CGFloat minTime;
///最长时长 默认为 1小时
@property (nonatomic,assign) CGFloat maxTime;
///资源类型 type
@property (nonatomic,assign) Enum_PYAblumManager_AssetType type;

///MARK: - 要继承自 PYAssetModel,
///默认 PYAssetModel
@property (nonatomic,strong) Class assetModelClass;

/**
 管理选中的assetModel
 
 @param clickedModel 被点击的model
 @param beOperatedArray 被操作的数组(请穿一个生命周期适当的数组)
 @param maxCount 最大可选中数量 (小于0 则为无最大选中限制)
 @param block 超出最大数量的回调
 @return 返回被操作后的数组
 */
+ (NSMutableArray <PYAssetModel *>*)getSelectAssetArrayWithClickedModel: (PYAssetModel *)clickedModel andBeOperated: (NSMutableArray <PYAssetModel *>*) beOperatedArray andMaxCount:(NSInteger) maxCount andOverTopBlock: (void(^)(NSArray <PYAssetModel *>*modelArray, BOOL isVoerTop))block;

///解析 asset
- (NSArray <PYAssetModel *>*)getAssetWithFetchResult:(PHFetchResult<PHAsset *>*)fetchResult;
@end

model中暴露的接口

@interface PYAssetModel : NSObject

///资源
@property (nonatomic,strong) PHAsset *asset;
///创建的时间
@property (nonatomic,strong) NSDate *creationDate;
///创建时间
@property (nonatomic,assign) NSInteger createTimeInteger;
///资源的类型
@property (nonatomic,assign) Enum_PYAblumManager_AssetType assetType;
/// 是否选中状态
@property (nonatomic,assign) BOOL isSelected;
/// 选中状态下 第几个被选中的 (没有选中状态下默认为 小于0)
@property (nonatomic,assign) NSInteger orderNumber;
/// 当前选中的model 是否超出最大值了;
@property (nonatomic,assign) BOOL isBeyondTheMaximum;

///image ID
@property (nonatomic,assign) PHImageRequestID imageRequestID;

///缩略图
@property (nonatomic,strong) UIImage *degradedImage;
///精致的缩略图
@property (nonatomic,strong) UIImage *delicateImage;
///原图
@property (nonatomic,strong) UIImage *originImage;

@end

3. 获取图片

  1. 分为缩略图,精致的压缩图,原图
  2. 主要是对PHAsset 进行了解析
///是否需自动调整图片方向 默认为true
@property (nonatomic,assign) BOOL shouldFixOrientation;

///获取封面照片
- (PHImageRequestID) getPotoWithAlbum: (PHFetchResult<PHAsset *> *)assetfetchResult
                         andPotoWidth: (CGFloat)Width
               andSortAscendingByDate: (BOOL)isSortAscendingByDate
                        andCompletion:(void (^)(UIImage *))completion;


/// 获取原图
- (PHImageRequestID) getOriginPoto:(PHAsset *)asset andCompletion: (void (^)(UIImage *))completion;


/**
 获取图片,默认获取的是缩略图,如果networkAccessAllowed 为true,则第二次调用block为获取到的原图
 
 @param asset 资源
 @param width 照片 期望宽度
 @param completion 图片加载完成的block 如果info[PHImageResultIsDegradedKey] 为 YES,则表明当前返回的是缩略图,否则是原图。
 @param progressBlock 进度相关
 @param networkAccessAllowed  参数控制是否允许 icloud 网络请求,默认为 NO,
 @return 返回了 PHImageRequestID
 */
- (PHImageRequestID) getPotoWithAsset: (PHAsset *)asset
                      andSetPotoWidth: (CGFloat)width
              andnetworkAccessAllowed: (BOOL) networkAccessAllowed
                        andCompletion: (void (^)(UIImage *photo,NSDictionary *info,BOOL isDegraded))completion
                          andProgress:(void (^)(double progress, NSError *error, BOOL *stop, NSDictionary *info))progressBlock;
@end

4. 接口汇总,所有的数据处理 都要通过单例PYAblum操作


///提供了一些常用方法,可以用单例中的manager 来做一些其他操作
@interface PYAblum : NSObject

#pragma mark - 单例
///swift 请调用这方法,获取
+ (PYAblum *) defaultAblum;
+ (PYAblum *) ablum;
/// “所有照片” 相册中,预先加载的个数
@property (nonatomic,assign) NSInteger allPhotoAbulmPreloadingCount;



#pragma mark -  关于相册

///相册管理类
@property (nonatomic,strong) PYAblumManager *ablumManager;

///自定义返回的model : 一定要继承自(PYAblumManagerModel)
@property (nonatomic,strong) Class ablumModelClass;





#pragma mark - 关于资源的
///资源管理者
@property (nonatomic,strong) PYAssetManager *assetManager;
///缓存的所有资源
@property (nonatomic,strong,readonly) NSArray <PYAssetModel *>*cacheAssetModelArray;
///所有相片相册中 转化后的资源model
@property (nonatomic,strong,readonly) NSArray <PYAssetModel *>*allPhotoAblumModelArray;

///缓存的所有照片
@property (nonatomic,strong,readonly) NSArray <UIImage *>* allPhotoAblumImageArray;
///自定义返回的model : 默认 PYAssetModel, 一定要继承自(assetModel)
@property (nonatomic,strong) Class assetModelClass;

//MARK: 关于选择资源的方法
/**
 管理选中的assetModel
 *
 @param clickedModel 被点击的model
 @param maxCount 最大可选中数量 (小于0 则为无最大选中限制)
 @param block 超出最大数量的回调
 @return 返回被操作后的数组
 */
+ (NSMutableArray <PYAssetModel *>*)getSelectAssetArrayWithClickedModel: (PYAssetModel *)clickedModel andMaxCount:(NSInteger) maxCount andOverTopBlock: (void(^)(NSArray <PYAssetModel *>*modelArray, BOOL isVoerTop))block;
/// 选被中的assetModel
@property (nonatomic,strong) NSMutableArray <PYAssetModel*>*selectedAssetModelArray;
/// 消除选中其中的某个asset
- (void)removeSelectedAssetWith:(PYAssetModel *)asset;
/// 清理 selectedAssetModelArray
- (void)removeSelectedAssetModelArray;






#pragma mark - image 管理类
@property (nonatomic,strong) PYImageManager *imageManager;






#pragma mark - 常用 方法
/**
 获取相册中的图片,其中不包含 视频的内容
 
 @param w image 的大小(assetLastModelBlock 调用中 image的大小)
 @param assetFirstModelBlock 缩略图
 @param assetLastModelBlock 精致些的图片
 */
- (void) getAblumAllImageWithWidth: (CGFloat)w andBlock: (void(^)(NSArray <PYAssetModel *>*assetFirstModelArray, NSArray <UIImage*>*degradedArray)) assetFirstModelBlock andBlock: (void(^)(NSArray <PYAssetModel *>*assetLastModelArray, NSArray <UIImage*>*originArray)) assetLastModelBlock;


/**
 * 获取所有相册 并获取相册的封面
 @param getAblumblock 获取相册
 @param w image的宽度,高度会自动比例计算
 * ablumModel 默认为 PYAblumModel, 如果,自定义,请给self.ablumModelClass赋值
 */
- (void) getAbulmSetCoverImageWithImageWidth: (CGFloat)w andBlock: (void(^)(NSArray <PYAblumModel*> *ablumModel))getAblumblock;


/**
 获取资源数组中的某张图片
 * 默认做了缓存,(index先从缓存中获取,再网络获取)
 @param assetArray 资源集合
 @param index 要获取的资源的下标
 @param width image 的宽度,如果width <= 0 或者 width 大于 image原有宽度,则返回原图
 */
- (void) getPhotoWithAssetArray: (NSArray <PHAsset *>*)assetArray andIndex: (NSInteger) index andImageSize: (CGFloat)width andCallBack: (void(^)(UIImage *))callBack;

/**
 获取资源数组中的某张图片
 * 默认做了缓存,(index先从缓存中获取,再网络获取)
 @param assetArray 资源集合
 @param index 要获取的资源的下标
 @param width image 的宽度,如果width <= 0 或者 width 大于 image原有宽度,则返回原图
 */
- (void) getPhotoWithAssetModelArray: (NSArray <PYAssetModel *>*)assetArray andIndex: (NSInteger) index andImageSize: (CGFloat)width andCallBack: (void(^)(PYAssetModel *))callBack;


/**
 时间排序
 
 @param modelArray model array
 @param isASC 是否升序,(最近期 照片 在后面)
 */
- (NSArray <PYAssetModel *>*) sortWithModelArray: (NSArray <PYAssetModel *>*)modelArray andIsASC: (BOOL)isASC ;

///删除 缓存的缩略图 及预览图
- (void)removeCache;

@end

3. UI处理

1. 图片的选择

有了数据,那么UI就好说了,对collectionView的封装就看demo吧
点我下载demo

3. PYKit
Web note ad 1