优雅的创建一个相册管理类(兼容AssetsLibrary和PhotoKit)

本文介绍了一个自定义相册管理类的实现,兼容iOS8之前的ALAssetsLibrary和之后的PhotoKit。通过封装,提供获取相册分组和图片信息的功能,同时展示了如何在项目中同时使用两者。文中包含关键类的解释和代码示例。
摘要由CSDN通过智能技术生成

在这篇文章的基础上,我github开源了一个自定义相册,进行了大量优化以及封装,配合着demo去看,会更容易理解。地址:https://github.com/lovisty/YFPhotoAlbum


越来越多的app选择图片的时候,放弃iOS默认的界面,使用自定义相册去展示,并供用户选择。做到这些,肯定少不了一个获取相册分组和每个分组里的图片的类,这里要展示一下一个Mange应该做的工作。

主要有两个工作,第一,获取相册分组。第二获取每个分组里的照片信息。这里针对iOS8之前的ALAssetsLibrary 和之后的PhotoKit进行阐述。

ALAssetsLibrary

先陈述一下几个类的意义

ALAssetsLibrary:
可以实现查看相册列表,增加相册分组,保存图片到相册等功能。


ALAssetsGroup:
包含了相册分组的信息,分组名称,分组类型,封面图片,分组内资源的数量等信息。


ALAsset:
包含了某个分组里的资源文件,以及图片缩略图,资源类型等其他资源信息。


ALAssetRepresentation:
是通过ALAsset的 defaultRepresentation 方法获取的一个类。
包含了资源文件的更加详细的信息,比如,大小,尺寸,高清图等。
如果在 ALAsset 里找不到,那就到 ALAssetRepresentation 里去逛逛吧,总有新的发现。

看看效(mei)果(nv):
这里写图片描述

show代码

.h 文件

#import <Foundation/Foundation.h>
#import <AssetsLibrary/AssetsLibrary.h>

typedef void(^AssetsLibraryInfoBlock)(NSArray *groupArray);
typedef void(^ALAssetsBlock)(NSArray *photoArray);

@interface PXPhotoAlbumManger : NSObject

@property (nonatomic, strong) ALAssetsLibrary *assetsLibrary;
@property (nonatomic, copy) AssetsLibraryInfoBlock  assetsLibraryInfo; //获取分组列表信息
@property (nonatomic, copy) ALAssetsBlock  photosInfoBlock; //获取分组里面的资源信息

- (void)allPhotoGroup:( ALAssetsGroupType )groupType  assetsLibraryInfo:(AssetsLibraryInfoBlock)assetsLibraryInfo ALAssetsoInfo:(ALAssetsBlock)ALAssetsoInfo;

- (void)allPhotoInALAssetsGroup:(ALAssetsGroup *)assetsGroup ALAssetsoInfo:(ALAssetsBlock)ALAssetsoInfo;

.m 文件


获取所有分组信息,并获取其中一个指定的分组里的资源

- (void)allPhotoGroup:( ALAssetsGroupType )groupType  assetsLibraryInfo:(AssetsLibraryInfoBlock)assetsLibraryInfo ALAssetsoInfo:(ALAssetsBlock)ALAssetsoInfo{

    NSMutableArray *groupArray = [NSMutableArray array];

    self.assetsLibrary = [[ALAssetsLibrary alloc] init];
    @weakify(self)
    [self.assetsLibrary enumerateGroupsWithTypes:groupType usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
      @strongify(self)
        if (group) {
            if ([group numberOfAssets] > 0) {
                [groupArray addObject:group];
            }
        }else{
            NSArray *resultArray = [[groupArray reverseObjectEnumerator] allObjects];
            assetsLibraryInfo(resultArray);
            //第一次默认遍历第一个相册分组 用于默认展示
            [self allPhotoInALAssetsGroup:[resultArray firstObject] ALAssetsoInfo:^(NSArray *photosInfoArray) {
                ALAssetsoInfo(photosInfoArray);
            }];
        }
    } failureBlock:^(NSError *error) {

    }];
}

获取指定分组里的资源信息

- (void)allPhotoInALAssetsGroup:(ALAssetsGroup *)assetsGroup ALAssetsoInfo:(ALAssetsBlock)ALAssetsoInfo{
    NSMutableArray *assetsArray = [NSMutableArray array];
    [assetsGroup enumerateAssetsWithOptions:NSEnumerationReverse usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {

        if (result != nil) {
            [assetsArray addObject:result];
        }else{
            ALAssetsoInfo(assetsArray);
        }
    }];
}

调用:
第一次调用,默认展示一个相册分组里的内容(遍历分组获取图片是一个耗时操作,所以不要一次去获取所有分组里的图片。如果项目有需求,可以考虑放到子线程进行。)

   @weakify(self)
    [self.manger allPhotoGroup:ALAssetsGroupAll assetsLibraryInfo:^(NSArray *groupArray) {
        @strongify(self)
        self.groups = groupArray;
        [self.view addSubview:self.photoAlbumView];
    } ALAssetsoInfo:^(NSArray *photoArray) {
        @strongify(self)
        [self.assetsImages addObjectsFromArray:photoArray];
        [self.collectionView reloadData];
    }];

切换相册分组的时候调用

     @weakify(self)
    [self.manger allPhotoInALAssetsGroup:group ALAssetsoInfo:^(NSArray *photoArray) {
        @strongify(self)
        [self.assetsImages addObjectsFromArray:photoArray];
        [self.collectionView reloadData];
    }];

在看看效果
这里写图片描述
当然这些只是基本的相册展示,实际项目里还需要有一些其他东西,比如选择(单选和多选)。你肯定也许需要这些方法:

AssetsGroup

[group valueForProperty:ALAssetsGroupPropertyName];//分组名称(一般用于显示)
[group valueForProperty:ALAssetsGroupPropertyType];//分组类型(用于获取分组里的资源)
[group valueForProperty:ALAssetsGroupPropertyPersistentID];//分组ID(用于区分不同的分组)

ALAsset

[asset valueForProperty:ALAssetPropertyType];//获取资源类型(ALAssetTypePhoto,ALAssetTypeVideo,ALAssetTypeUnknown)

[asset valueForProperty:ALAssetPropertyAssetURL];//获取资源的URL(可用于区分是否为同一张图片【因为不同分组里的图片可能相同】)

[asset valueForProperty:ALAssetPropertyDuration];//如果是视频文件,返回视频的长度

[asset valueForProperty:ALAssetPropertyLocation];//返回资源的地理位置

[asset valueForProperty:ALAssetPropertyOrientation];//返回拍摄方向  ALAssetPropertyOrientation为枚举类型

[asset valueForProperty:ALAssetPropertyRepresentations];//获取资源的描述信息

ALAssetRepresentation

ALAssetRepresentation *representation = [asset defaultRepresentation];

[representation dimensions];//返回图片的尺寸

[representation size]; //返回图片的大小,即所占用物理空间

[representation filename]; //文件的名字

如果你的app已经不再支持iOS8一下的操作系统,那么你就可以任性的不再使用ALAssetsLibrary,使用iOS8新的PhotoKit框架。使用起来更方便,获取的信息更完整,也更高效。下面更新将对PhotoKit进行一些解析。


PhotoKit
所有的 PhotoKit
对象都是继承自 PHObject 抽象基类
同样,在展示代码前,先熟悉一下下面几个类


PHCollection:
这是一个抽象类,我们不会对其进行实例化,当然这样做也没什么意义。它的两个子类 PHAssetCollection or PHCollectionList 才是我们想要的。


PHAssetCollection:
表示一个相册或者一个时刻,或者是一个由系统提供的一系列智能相册(SmartAlbum),
如:最近添加,最近删除,屏幕截图,收藏等。


PHCollectionList:
表示一组 PHCollections 。它可能同时包含 PHAssetCollection 和 PHCollectionList ,所以它本身也是一个PHCollection。
这里有些不容易理解的,首先PHCollectionList会包含PHAssetCollection信息,比如用户自己创建的相册。
同时也会包含具有继承关系的一系列相册(PHCollectionList),比如系统相册里的时刻-年度。


PHFetchResult:

表示一系列的资源结果集合。
当你使用PHAsset,PHCollection,PHAssetCollection 和 PHCollectionList类检索对象,都会返回一个PHFetchResult对象,你所需要的信息,就在这个对象里。
另外,虽然这是同步操作,但官方告诉我们,不用去担心性能问题,即使是获取大量数据,依然能够保持很好的性能。


PHAsset:
代表照片库中的一个资源或图片,或视频,另外它会包含iCloud里的内容。这点和 ALAsset 有些类似。


PHImageManager:

与ALAsset 的不同之处在乎,通过 PHAsset 可直接获取资源的信息。但是PHAsset不行,它要通过 PHImageManager 进行获取。
PHImageManager 用于处理图片或者视频资源的加载,可以获取到图片的缩略图,原图,以及用来播放的视频文件。另外一个亮点就是资源的加载过程带有缓存处理,下一次加载的时候就会变得很快。对于我们想要的资源,可以通过传入一个 PHImageRequestOptions 控制输出,得到我们想要的样子。


PHImageRequestOptions:

当然就是控制加载资源的一系列参数了。其中有 synchronous(是否为同步请求,默认NO), PHImageRequestOptionsVersion , PHImageRequestOptionsDeliveryMode , PHImageRequestOptionsResizeMode , CGRect , networkAccessAllowed , PHAssetImageProgressHandler 等一系列条件限制。


PhotoKit最直观的展示,是不是比上面的强大多了
这里写图片描述

熟悉了上面的这些类(如果不熟悉,可以结合下面的代码理解一下),我们看一下具体的代码。

Show Code

.h

#import <Foundation/Foundation.h>

typedef void(^PhotoKitAllGrougsBlock)(NSArray *groupArray);
typedef void(^PHImagesBlock)(NSArray *photoArray);

@interface PXPhotoKitManger : NSObject

@property (nonatomic, strong) NSMutableArray *photosArray;
@property (nonatomic, strong) NSMutableArray *assetCollections;

//如果所有group(包含智能相册和用户自己创建的相册)
- (void)allGroupInfo:(PhotoKitAllGrougsBlock)allGrougsInfo PHImagesInfo:(PHImagesBlock)PHImagesInfo;

- (void)allPhotoInPHAssetGroup:(PHAssetCollection *)assetCollection PHImagesInfo:(PHImagesBlock)PHImagesInfo;

//获取相册的封面【这里不像AssetsLibrary那样简单,需要我们遍历相册取第一张图片】
- (void)theCoverPhotoInPHAssetGroup:(PHAssetCollection *)assetCollection PHImagesInfo:(PHImagesBlock)PHImagesInfo;


.m

获取相册分组 并默认获取一个指定相册里的内容

#pragma mark - 获取相册分组
- (void)allGroupInfo:(PhotoKitAllGrougsBlock)allGrougsInfo PHImagesInfo:(PHImagesBlock)PHImagesInfo{

    self.assetCollections = [NSMutableArray array];
    self.photosArray = [NSMutableArray array];
    PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
    fetchOptions.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"localizedTitle" ascending:YES]];

    PHFetchResult *smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
    PHFetchResult *userAlbums = [PHCollectionList fetchTopLevelUserCollectionsWithOptions:nil];

    [self obtainAllAssetCollectionWithPHFetchResult:@[smartAlbums,userAlbums] allGrougsInfo:allGrougsInfo PHImagesInfo:PHImagesInfo];
}

- (void)obtainAllAssetCollectionWithPHFetchResult:(NSArray *)resultAlbums allGrougsInfo:(PhotoKitAllGrougsBlock)allGrougsInfo PHImagesInfo:(PHImagesBlock)PHImagesInfo{

    PHFetchResult *smartAlbums = resultAlbums[0];
    PHFetchResult *userAlbums = resultAlbums[1];

    for (int i = 0; i < userAlbums.count; i++) {
        PHCollection *collection = userAlbums[i];
        if ([collection isKindOfClass:[PHAssetCollection class]]) {
            PHAssetCollection *assetCollection = (PHAssetCollection *)collection;
            [self.assetCollections addObject:assetCollection];
        }
    }

    for (NSInteger i = 0; i < smartAlbums.count; i++) {

        PHCollection *collection = smartAlbums[i];
        if ([collection isKindOfClass:[PHAssetCollection class]]) {
            PHAssetCollection *assetCollection = (PHAssetCollection *)collection;
            [self.assetCollections addObject:assetCollection];
        }
    }

    allGrougsInfo(self.assetCollections);
    [self allPhotoInPHAssetGroup:self.assetCollections[0] PHImagesInfo:^(NSArray *photos) {
        [self.photosArray addObjectsFromArray:photos];
        PHImagesInfo(photos);
    }];
}


#pragma mark - 获取一个指定相册里的内容
- (void)allPhotoInPHAssetGroup:(PHAssetCollection *)assetCollection PHImagesInfo:(PHImagesBlock)PHImagesInfo{

    PHFetchResult *fetchResult = [PHAsset fetchAssetsInAssetCollection:assetCollection options:nil];
    //如果相册里没有照片,直接返回nil
    if (fetchResult.count <=0) {
        PHImagesInfo(nil);
        return;
    }
    PHImageManager  *manger = [[PHImageManager  alloc]init];
    NSMutableArray *photos = [NSMutableArray array];

    PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
    options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;

    __block NSInteger tempMark = 0;
    for (NSInteger i = 0; i < fetchResult.count; i++) {
        // 获取一个资源(PHAsset)
        PHAsset *asset = fetchResult[i];
        [manger requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeDefault options:options resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
            [photos addObject:result];

            if (tempMark == fetchResult.count-1) {
                PHImagesInfo(photos);
            }
            tempMark++;
        }];
    }
}

#pragma mark - 获取相册封面

- (void)theCoverPhotoInPHAssetGroup:(PHAssetCollection *)assetCollection PHImagesInfo:(PHImagesBlock)PHImagesInfo{


    NSLog(@"%@  %ld  ",assetCollection.localizedTitle,(long)assetCollection.assetCollectionSubtype);

    PHFetchResult *fetchResult = [PHAsset fetchAssetsInAssetCollection:assetCollection options:nil];

    if (fetchResult.count<=0) {
        UIImage *image = [UIImage imageNamed:@"picture"];
        PHImagesInfo(@[image]);
        return;
    }

    PHImageManager  *manger = [[PHImageManager  alloc]init];

    // 获取一个资源(PHAsset)
    PHAsset *asset = fetchResult[0];
    NSMutableArray *photos = [NSMutableArray array];
    PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
    options.synchronous = YES;//为了效果,我这里选择了同步 因为只获取一张照片,不会对界面产生很大的影响
    [manger requestImageForAsset:asset targetSize:CGSizeMake(50, 50) contentMode:PHImageContentModeDefault options:options resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
        [photos addObject:result];
        PHImagesInfo(photos);
    }];
}

使用的时候

[self.manger allGroupInfo:^(NSArray *groupArray) {
        __strong __typeof(weakSelf)self = weakSelf;
        self.groups = groupArray;
    } PHImagesInfo:^(NSArray *photoArray) {
        //第一张代表拍照的图片
        UIImage *camera = [UIImage imageNamed:@"camera"];
        if (camera) {
            [self.photos addObject:camera];
        }
        [self.photos addObjectsFromArray:photoArray];
        [self.collectionView reloadData];
    }];

切换的时候

- (void)switchAssetCollectionGroup:(PHAssetCollection *)assetCollection{
    [self.manger allPhotoInPHAssetGroup:assetCollection PHImagesInfo:^(NSArray *photoArray) {
        UIImage *camera = [UIImage imageNamed:@"camera"];
        if (camera) {
            [self.photos addObject:camera];
        }
        [self.photos addObjectsFromArray:photoArray];
        [self.collectionView reloadData];
    }];
}

另外,如何在一个项目里同时使用AssetsLibrary和PhotoKit。这里就需要一个具有包容精神的Model,可以接收来自不同地方的数据。

我这里给model分别提供了两个方法

声明:

+ (NSArray *)transformALAssetsGroupToModelFrom:(NSArray *)groups;

+ (NSArray *)transformPHAssetCollectionToModelFrom:(NSArray *)collections;

实现

//AssetsLibrary 赋值
+ (NSArray *)transformALAssetsGroupToModelFrom:(NSArray *)groups{

    NSMutableArray *groupArray = [NSMutableArray array];

    for (ALAssetsGroup *group in groups) {
        PXPhotoAlbumModel *model = [[PXPhotoAlbumModel alloc]init];
        model.albumName =  [group valueForProperty:ALAssetsGroupPropertyName];
        UIImage* image = [UIImage imageWithCGImage:group.posterImage];
        model.coverImage = image;
        model.photoCount = group.numberOfAssets;
        model.groupType =  [[group valueForProperty:ALAssetsGroupPropertyType] integerValue];
        model.group = group;
        [groupArray addObject:model];
    }
    return [groupArray copy];
}

//PhotoKit 赋值
+ (NSArray *)transformPHAssetCollectionToModelFrom:(NSArray *)collections{

    NSMutableArray *groupArray = [NSMutableArray array];

    for (PHAssetCollection *assetCollection in collections) {
        PXPhotoAlbumModel *model = [[PXPhotoAlbumModel alloc]init];
        model.albumName = assetCollection.localizedTitle;
        PXPhotoKitManger *manger = [[PXPhotoKitManger alloc]init];
        //获取封面
        [manger theCoverPhotoInPHAssetGroup:assetCollection PHImagesInfo:^(NSArray *photoArray) {
            model.coverImage = photoArray[0];
        }];
        PHFetchResult *assetsFetchResult = [PHAsset fetchAssetsInAssetCollection:assetCollection options:nil];
        //获取照片个数
        model.photoCount = assetsFetchResult.count;
        model.assetCollection = assetCollection;
        [groupArray addObject:model];
    }

    return groupArray;
}

这样,我们获取想要的值,都可以通过这个model直接获取。


以上就是AssetsLibrary和PhotoKit的基本概念,使用,以及简单封装。更多更深入的东西还有待我们发掘。有相关疑问欢迎联系我,大家一起讨论,相互学习。
QQ:617267337

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值