图片选择器(Photos / AssetsLibrary)

        有没有人和我一样,在最开始接触iOS开发的时候,碰到图片选择器用的是UIImagePickerController,后来发现产品和设计不会按照系统页面来设计,这个时候去github或者cocochina上找一个第三方库,修修改改,用到哪改到哪,管中窥豹,由于定制了计划,每月写两篇博客,借此机会,把这个框架梳理一下。

        首先明确一点,这篇文章是了解一下 AssetsLibrary和Photos框架,并就实例进行说明解析,很简单的一个小demo,并不是现成拿过来直接就能用。(Photos虽然比AssetsLibrary用起来舒服太多,但是它是iOS8.0之后才出来的,注意一下适配)

        市场上绝大部分图片选择器都大同小异,例如微信和微博:


图片选择器(Photos / AssetsLibrary)_第1张图片
wechat.png
图片选择器(Photos / AssetsLibrary)_第2张图片
微博.png

其实就是两个列表,一个相册列表,一个图片列表,当然也可以直接理解为一个tableView,一个collectionView。

Photos


1.1、UITableView

  • 授权、获取相册集合
    /*
     *  获取用户授权
     *
     *  PHAuthorizationStatus  授权状态
     */
    [PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
        
        /*
         *  PHAuthorizationStatusNotDetermined  用户还没有进行授权操作
         *  PHAuthorizationStatusRestricted     用户关闭了访问相册的权限
         *  PHAuthorizationStatusDenied         拒绝用户访问这个应用程序有明确的图片数据
         *  PHAuthorizationStatusAuthorized     用户已授权
         */
        
        if (status == PHAuthorizationStatusNotDetermined || status == PHAuthorizationStatusRestricted) {
            /*
             *  未通过用户授权,可弹出用户引导,UIAlertView(请在设备的\"设置-隐私-照片\"中允许访问照片)
             */
        } else {
            /*
             *  用户已授权
             *
             *  获取相册集合,将其加入到tableView数据源中
             */
            PHFetchResult *album = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum
                                                                            subtype:PHAssetCollectionSubtypeAlbumRegular
                                                                            options:nil];
            [album enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                PHAssetCollection *collection = (PHAssetCollection *)obj;

                // 这里需要注意下,我们数据源中存的是PHAssetCollection
                [self.groups addObject:collection]; 
                [self.tableView reloadData];
            }];
        }
    }];
  • 信息对照表
属性 描述
PHAsset 代表照片库中的一个资源,通过 PHAsset 可以获取和保存资源
PHFetchOptions 获取资源时的参数,可以传 nil,即使用系统默认值
PHAssetCollection PHCollection 的子类,表示一个相册或者一个时刻(例:最近删除,视频列表,收藏等)
PHFetchResult 表示一系列的资源结果集合(此处是相册集合),从PHCollection 的类方法中获得
PHImageManager 用于处理资源的加载,加载图片的过程带有缓存处理,可以通过传入一个 PHImageRequestOptions 控制资源的输出尺寸等规格(上面cell刷新时代码)
PHImageRequestOptions 控制加载图片时的一系列参数
  • 枚举
    typedef NS_ENUM(NSInteger, PHAssetCollectionType) {
        PHAssetCollectionTypeAlbum      = 1,      // 从iTunes同步的相册,以及用户在 Photos 中自己建立的相册
        PHAssetCollectionTypeSmartAlbum = 2,      // 相机相册
        PHAssetCollectionTypeMoment     = 3,      // 自动生成的时间分组的相册
    } PHOTOS_ENUM_AVAILABLE_IOS_TVOS(8_0, 10_0);
    typedef NS_ENUM(NSInteger, PHAssetCollectionSubtype) {
        
        // 用户在 Photos 中创建的相册,也就是我所谓的逻辑相册
        PHAssetCollectionSubtypeAlbumRegular         = 2,
        // 使用 iTunes 从 Photos 照片库或者 iPhoto 照片库同步过来的事件
        PHAssetCollectionSubtypeAlbumSyncedEvent     = 3,
        // 使用 iTunes 从 Photos 照片库或者 iPhoto 照片库同步的人物相册。
        PHAssetCollectionSubtypeAlbumSyncedFaces     = 4,
        // 做了 AlbumSyncedEvent 应该做的事
        PHAssetCollectionSubtypeAlbumSyncedAlbum     = 5,
        // 从相机或是外部存储导入的相册
        PHAssetCollectionSubtypeAlbumImported        = 6,
        
        // 用户的 iCloud 照片流
        PHAssetCollectionSubtypeAlbumMyPhotoStream   = 100,
        // 用户使用 iCloud 共享的相册
        PHAssetCollectionSubtypeAlbumCloudShared     = 101,
        
        // 文档解释为非特殊类型的相册,主要包括从 iPhoto 同步过来的相册
        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,
        // 在可兼容的设备上使用景深摄像模式拍的照片
        PHAssetCollectionSubtypeSmartAlbumDepthEffect PHOTOS_AVAILABLE_IOS_TVOS(10_2, 10_1) = 212,
        // 包含所有的Live Photo资源
        PHAssetCollectionSubtypeSmartAlbumLivePhotos PHOTOS_AVAILABLE_IOS_TVOS(10_3, 10_2) = 213,
        PHAssetCollectionSubtypeSmartAlbumAnimated PHOTOS_AVAILABLE_IOS_TVOS(11_0, 11_0) = 214,
        PHAssetCollectionSubtypeSmartAlbumLongExposures PHOTOS_AVAILABLE_IOS_TVOS(11_0, 11_0) = 215,
        
        //包含所有类型
        PHAssetCollectionSubtypeAny = NSIntegerMax
    } PHOTOS_ENUM_AVAILABLE_IOS_TVOS(8_0, 10_0);
  • cell展示、赋值
- (void)refreshWithPHAssetCollection:(PHAssetCollection *)collection {
    // 按时间生序
    PHFetchOptions *option = [[PHFetchOptions alloc] init];
    option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
    
    /*
     *  获取相册实体
     *
     *  根据实体拿到展示的三个属性:标题、图片个数、相册的第一张图片
     */
    PHFetchResult *result = [PHAsset fetchAssetsInAssetCollection:collection options:option];
    self.textLabel.text = collection.localizedTitle;
    self.detailTextLabel.text = [NSString stringWithFormat:@"%ld",[result count]];
    [result enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        PHAsset *asset = (PHAsset *)obj;

        PHImageRequestOptions *imageOption = [[PHImageRequestOptions alloc] init];
        imageOption.resizeMode = PHImageRequestOptionsResizeModeFast;

        /*
         * 坑一 networkAccessAllowed
         
         * 是否允许网络访问,默认为NO
         * 主要只是否可以从iCloud中下载图像,如果iPhone开启iCloud优化话存储空间,设置为NO是拿不到图片的,这里也只一个坑,需要注意一下
         */
        imageOption.networkAccessAllowed = YES;

        /*
         * 坑二 synchronous
         
         * 是否同步处理一个图像请求,默认是NO
         * 这里一般设置为NO,requestImageForAsset 请求就会有两次回调。第一次返回一个低质量的图片(缩略图),用于占位显示;第二次返回的是一个高质量的图(原图)
         * 如果设置为YES,请求就只有一次的回调,返回一个高质量的图(原图),会有卡顿现象(线程阻塞)
         */
        imageOption.synchronous = NO;

        [[PHImageManager defaultManager] requestImageForAsset:asset
                                                   targetSize:CGSizeMake(100.0f, 100.0f)
                                                  contentMode:PHImageContentModeAspectFit
                                                      options:imageOption
                                                resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
            self.imageView.image = result;
        }];
        *stop = YES;
    }];
}
  • 相册列表结果展示
图片选择器(Photos / AssetsLibrary)_第3张图片
demo_相册列表.PNG

1.2、UICollectionView

在上述表格中我们曾介绍过PHFetchOption,它表示一个相册集合。
获取collectionView的数据源其实很简单,与上述我们在cell中获取图片封面一样,通过PHAssetCollection就能拿到。

    // 照片按照时间生序排列
    PHFetchOptions *option = [[PHFetchOptions alloc] init];
    option.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:NO]];
    
    // 获取照片结果集合,将其添加至数据源数组
    PHFetchResult *result = [PHAsset fetchAssetsInAssetCollection:self.colletion options:option];
    [result enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        PHAsset *asset = (PHAsset *)obj;
        [self.assets addObject:asset];
        if (idx == result.count - 1) {
            [self.collectionView reloadData];
        }
    }];


AssetsLibrary

2.1、TableView

  • 获取相册集合
/** 代表整个设备中的资源库(照片库 */
@property (nonatomic, retain) ALAssetsLibrary *assetsLibrary;

// 实例化一个对象
self.assetsLibrary = [[ALAssetsLibrary alloc] init];

// 获取相册
[self.assetsLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
    if (group) {
        // 将相册图片张数不为零的添加到数据源中
        if (group.numberOfAssets > 0) {
            [_groups addObject:group];
        }
    } else {
        [self.tableView reloadData];
    }
} failureBlock:^(NSError *error) {    
}];
  • 更新cell
- (void)refresh:(ALAssetsGroup *)assetsGroup {
    // 相册封面赋值
    size_t height = CGImageGetHeight(assetsGroup.posterImage);
    float scale = height / 60.0f;
    UIImage *groupImage = [UIImage imageWithCGImage:assetsGroup.posterImage
                                              scale:scale
                                        orientation:UIImageOrientationUp];
    self.imageView.image = groupImage;
    
    /*
     *  property
     *
     *  ALAssetsGroupPropertyName           相册的名字
     *  ALAssetsGroupPropertyType           相册的类型
     *  ALAssetsGroupPropertyPersistentID   相册的存储id
     *  ALAssetsGroupPropertyURL            相册存储的位置地址
     */
    self.textLabel.text = [assetsGroup valueForProperty:ALAssetsGroupPropertyName];
    
    // 相册图片张数
    self.detailTextLabel.text = [NSString stringWithFormat:@"%ld", (long)[assetsGroup numberOfAssets]];
    self.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
  • 信息对照表
属性 描述
AssetsLibrary 代表整个设备中的资源库(照片库),通过 AssetsLibrary 可以获取照片和视频
ALAssetsGroup 通过 ALAssetsGroup 可以获取某个相册的信息,相册下的资源,同时也可以对某个相册添加资源
ALAsset 映射照片库中的一个照片或视频,通过 ALAsset 可以获取某个照片或视频的详细信息,或者保存照片和视频
ALAssetRepresentation 对 ALAsset 的封装(但不是其子类),可以更方便地获取 ALAsset 中的资源信息
  • 枚举类型
    enum {
        ALAssetsGroupLibrary       // 本地和 iTunes
        ALAssetsGroupAlbum         // 从iTunes同步来的照片,不包括共享的(例如从各个软件中保存下来的图片)
        ALAssetsGroupEvent         // 同步到 iTunes 的(包括相机导入的)
        ALAssetsGroupFaces         // 同步 iTunes 的
        ALAssetsGroupSavedPhotos   // 相机胶卷
        ALAssetsGroupPhotoStream   // 照片流
        ALAssetsGroupAll           // 全部相册
    };
  • collectionView
    /*
     *  将相册中的图片放入collectionView数据源中
     *
     *  NSEnumerationConcurrent  正序遍历
     *  NSEnumerationReverse     反向遍历
     */
    [self.assetsGroup enumerateAssetsWithOptions:NSEnumerationReverse usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
        if (result) {
            if ([[result valueForProperty:ALAssetPropertyType] isEqual:ALAssetTypePhoto]) {
                //只访问照片,不访问视频
                [self.assets addObject:result];
            }
        } else {
            [self.collectionView reloadData];
        }
    }];

后续还会整理一下拍照和视频录制的一些内容,欢迎点赞

参考地址:
iOS 开发之照片框架详解
Photos 框架实践以及坑

你可能感兴趣的:(图片选择器(Photos / AssetsLibrary))