图片选择器(Photos / AssetsLibrary)

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

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

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


wechat.png
微博.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;
    }];
}
  • 相册列表结果展示
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 框架实践以及坑

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容