使用AssetsLibrary库实现系统类似照片浏览器
由于项目需求,要实现一个多项选择的图片浏览器,使用系统的UIImagePickerController只能选择单张图片,于是决定通过AssetsLibrary自定义。
1、AssetsLibrary的引入
引用AssetsLibrary的方法同引入iOS系统提供的其他framework一样,在“General”下滚动至”Linked Frameworks and Libraries”或者在”Build Phases”标签页中,展开”Link Binary With Libraries”,然后点击下面的加号,在弹出的对话框中输入AssetsLibrary即可引入,如下图所示:
2、AssetsLibrary组成介绍
其实AssetsLibrary的使用非常简单,网络上也有很多关于该framework的文章,关于AssetsLibrary的介绍就引用别人写的一篇文章里的内容吧。(http://www.jianshu.com/p/14d04b3ef00a 文/TEASON) AssetsLibrary 的组成比较符合照片库本身的组成,照片库中的完整照片库对象、相册、相片都能在 AssetsLibrary 中找到一一对应的组成,这使到 AssetsLibrary 的使用变得直观而方便。
1、AssetsLibrary:
代表整个设备中的资源库(照片库),通过 AssetsLibrary 可以获取和包括设备中的照片和视频
2、ALAssetsGroup:
映射照片库中的一个相册,通过 ALAssetsGroup 可以获取某个相册的信息,相册下的资源,同时也可以对某个相册添加资源。
3、ALAsset:
映射照片库中的一个照片或视频,通过 ALAsset 可以获取某个照片或视频的详细信息,或者保存照片和视频。
4、ALAssetRepresentation:
ALAssetRepresentation 是对 ALAsset 的封装(但不是其子类),可以更方便地获取 ALAsset 中的资源信息,每个 ALAsset 都有至少有一个 ALAssetRepresentation 对象,可以通过 defaultRepresentation 获取。而例如使用系统相机应用拍摄的 RAW + JPEG 照片,则会有两个 ALAssetRepresentation,一个封装了照片的 RAW 信息,另一个则封装了照片的 JPEG 信息。
3、图片浏览器的实现
1、授权的检查
要想访问相册功能,根据iOS一惯的作风,肯定是要有授权的。如何检查授权呢?就是使用ALAssetsLibrary类中的静态方法—authorizationStatus,该方法返回一个ALAuthorizationStatus类型的值,如果该值为ALAuthorizationStatusRestricted或ALAuthorizationStatusDenied,则表明没有授权,那么我们也无法再继续下去。
2、相册列表的获取
取得授权之后,我们便可以使用ALAssetsLibrary类中常用的enumerateGroupsWithTypes:usingBlock:failureBlock:方法枚举出系统中所有的相册集,该方法接受一个参数,从方法名上也可以看出来,该参数是要求我们指定一个要枚举的相册的类型,选项有ALAssetsGroupLibrary,AlAssetsGroupAlbum,ALAssetsGroupEvent,ALAssetsGroupFaces,ALAssetsGroupSavedPhtos,ALAssetsGroupPhotoStream,ALAssetsGroupAll,根据需要传递相应的参数即可。至于usingBlock的定义是这样的:typedef void (^ALAssetsLibraryGroupsEnumerationResultsBlock)(ALAssetsGroup *group, BOOL *stop),传出的两个参数中,ALAssetsGroup代表一个相册集,stop则用于指示是否应该停止枚举。当我们取到的group为nil的时候,则表示所有的相册集已经枚举完毕,此时我们应该将*stop设置为NO,并进行后续相应的操作,如以下代码所示:
ALAuthorizationStatus authorizationStatus = [ALAssetsLibrary authorizationStatus];
if (authorizationStatus != ALAuthorizationStatusRestricted && authorizationStatus != ALAuthorizationStatusDenied) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
WEAKSELF;
[self.assetsLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
if(group)
[assetsGroupArray addObject:group];
else {
*stop = YES;
dispatch_async(dispatch_get_main_queue(), ^{
[wself.theTableView reloadData];
});
}
return ;
} failureBlock:^(NSError *error) {
[[[UIAlertView alloc] initWithTitle:nil message:error.localizedFailureReason delegate:nil cancelButtonTitle:I(@"确定") otherButtonTitles: nil] show];
}];
});
}else {
[[[UIAlertView alloc] initWithTitle:nil message:I(@"未取得授权,无法访问相薄") delegate:nil cancelButtonTitle:I(@"确定") otherButtonTitles: nil] show];
}
考虑到可能用户的相册会有很多,如果在主线程上进行枚举的话,会导致主UI卡死,所以采用GCD在非UI线程上执行该方法,待枚举结束之后,再在主线程上更新UITableView,将所获得的ALAssetsGroup实例存入到一个NSMutableArray当中。效果如下图所示:
在UITableView的代理方法cellForRowAtIndexPath中,我们可以使用ALAssetsGroup的posterImage来获取一张封面图片,并可以使用numberOfAssets方法来获取这个相册集中共有多少张照片,同时还能使用valueForProperty方法传入ALAssetsGroupPropertyName来获取到该相册的名字,代码如下所示:
ALAssetsGroup* group = (ALAssetsGroup*)[assetsGroupArray objectAtIndex:(int)indexPath.row];
[cell.posterImageView setImage:[UIImage imageWithCGImage:group.posterImage]];
[cell.assetsTitleLabel setText:[group valueForProperty:ALAssetsGroupPropertyName]];
[cell.assetsCountLabel setText:[NSString stringWithFormat:@"%ld", (long)group.numberOfAssets]];
3、获取指定相册中的图片
同样的,我们要获取对应相册中的图片,也可以使用ALAssetsGroup中提供的常用方法enumerateAssetsUsingBlock:方法,该方法的使用方式同ALAssetsLibrary中的枚举方法一模一样,该方法在Block中会返回一个ALAsset的实例,这个实例就相当于我们相册集中的一张图片了,我们同样可以使用一个NSMutableArray来将这个实例保存下来。代码如下所示:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
WEAKSELF;
[self.assetsGroup enumerateAssetsUsingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
if(result != nil)
[assetsMutableArray addObject:result];
else {
*stop = YES;
dispatch_async(dispatch_get_main_queue(), ^{
[wself.assetsCollectionView reloadData];
});
}
}];
});
同样,出于不卡死UI线程的考虑,我们同样在非UI线程上执行枚举操作。
得到了ALAsset实例之后,图片浏览器基本上也就实现完毕了,剩下的只需要把图片从ALAsset中取出来,然后按自己需要的方式展示出来即可,那怎么从ALAsset中取出图片呢?根据我自己的实现方式,我是先展示一张缩略图,用户点击之后再做另外的操作。从ALAsset中取得缩略图的方法很简单,ALAsset类中就提供了thumbnail和aspectRatioThumbnail两个能获取到缩略图的方法,至于两个方法的区别,自己去试一试就明白了,其实我自己也没有试过,所以不好意思误导读者。用户点击之后,一般可以预览一张完整的图,那应该要怎么做呢?这就要用到ALAssetRepresentation了,我们可以通过ALAsset中的常用方法defaultRepresentation获取到该实例,该例中就提供了获取图片大小、尺寸等的方法,如果要获取原图,则可以使用其中的fullResolutionImage方法来取得一个CGImageRef,从页赋值给UIImageView进行图片展示,我使用的是一个UICollectionView来展示图片,代码如下所示:
-(UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:IMAGEPICKERCOLLECTIONVIEWCELLIDENTIFIER forIndexPath:indexPath];
[cell.contentView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
UIImageView* imageView = [[UIImageView alloc] initWithFrame:cell.contentView.bounds];
[cell.contentView addSubview:imageView];
ALAsset* asset = (ALAsset*)[assetsMutableArray objectAtIndex:indexPath.row];
[imageView setImage:[UIImage imageWithCGImage:asset.aspectRatioThumbnail]];
return cell;
}
4、最终效果展示
AssetsLibrary的使用方法真的是非常简单,不过从iOS8开始, Apple公司提供了PhotoKit用来替代AssetsLibrary,并在iOS9中正式弃用AssetsLibray,不过管它呢,谁让它的使用方式so easy…最终效果如下所示: