iOS 自定义图片选择器 1 - PhotoKit

【 写在前面:笔者按照Instagram的图片选取器写了个小Demo,
该系列文章为笔者实现Demo的步骤,若有不正确的地方还望指出来,共同学习。
地址在最后】


iOS开发者对于图片选择器不会感到陌生,例如最常用的UIImagePicker就是系统提供的,可以实现简单的图片选取与编辑,当需要高度定制化的时候,就需要我们自己造轮子了。笔者就以Instagram为参考参考来造自己轮子。

当然,也能找到很多优秀的第三方,如:
MWPhotoBrowser:(https://github.com/mwaterfall/MWPhotoBrowser)
它是一个图片选取的框架,需要传入图片资源。我们的业务很多时候是直接取的系统的相册,而笔者造的轮子定位就是一个基于系统相册的自定义图片选择器。所以我们必须先了解下如何获取系统的图片资源。

在iOS 8以前想要与系统相册进行互动使用的是ALAsset,而在iOS9开始,苹果就不再推荐了。从iOS 8开始,苹果推出PhotoKit来取代ALAsset,PhotoKit还能够配合ICloud,且当下基本以iOS 8为最低版本,我们也就直接使用PhotoKit来进行开发。

使用选取器的基本的流程是(调用选取器->获取图片->展示图片->选取图片->回调并关闭),在实际流程中会有细节上的差异。

下面笔者会根据功能的先后,把主要使用到的方法依次列举出来。

1.权限的配置与获取

在iOS 10中需要添加额外的安全设定,否则程序会直接崩溃,我们用到了相册,所以需要在info.plist里添加NSPhotoLibraryUsageDescription的key值,其value为获取权限的描述,该描述会在设置中的相册权限展示。
要获取图片还要知道获取图片的权限是否开启,可调用以下方法:

[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
    /*当status等于PHAuthorizationStatusAuthorized表示用户已经授予了权限。
    该方法在应用初次询问时,会直接弹出系统的权限询问,
    用户操作后才会回调,所以不存在PHAuthorizationStatusNotDetermined
    (用户还未对权限进行选择)的状态。*/
}];

//当然,也可以使用下方的方法直接获取权限的状态
//PHAuthorizationStatus state = [PHPhotoLibrary authorizationStatus];

2.获取相册信息

在用到PhotoKit的地方需要包含其头文件

#import <Photos/Photos.h>

获取手机内所有的相册:

    //search collection data
    PHFetchResult * sysfetchResult = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum
                                                                              subtype:PHAssetCollectionSubtypeAlbumRegular
                                                                              options:nil];

遍历相册,获取需要的值,如相册的Title:

for (PHAssetCollection * assetCollection in sysfetchResult) {
     NSString * collectionTitle = assetCollection.localizedTitle;
}

获取相册所有图片的信息

/**get PHAsset from collection*/
- (NSArray *)getAssetWithCollection:(PHAssetCollection *)collection {
    //set fetchoptions
    PHFetchOptions * options = [[PHFetchOptions alloc] init];
    options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate"
                                                              ascending:YES]];
    
    //search
    NSMutableArray * assetArray = [NSMutableArray array];
    PHFetchResult * assetFetchResult = [PHAsset fetchAssetsInAssetCollection:collection
                                                                     options:options];
    
    for (PHAsset * asset in assetFetchResult) {
        [assetArray addObject:asset];
        
    }
    return assetArray;
}

3.获取图片

在PhotoKit中,单个图片的信息都保存在PHAsset中,也可以说每一个PHAsset就是一个图片的所有数据,例如宽、高、图片地址等等。
我们得通过PHAsset来获取图片

    PHImageRequestOptions * options = [[PHImageRequestOptions alloc] init];
    
    //图片的质量相关设置
    options.resizeMode = PHImageRequestOptionsResizeModeNone;
    options.deliveryMode = PHImageRequestOptionsDeliveryModeOpportunistic;
    
    //当图片为iCloud资源时,是否通过网络获取,默认为NO
    options.networkAccessAllowed = YES;
    
    //是否为同步的,默认为NO (这里笔者需要将其顺序加入集合,所以改为了YES)
    options.synchronous = YES;
    
    [options setProgressHandler:^(double progress, NSError *error, BOOL *stop, NSDictionary *info) {
//        [weakself requestProgress:progress];
    }];
    _imagesArray = [NSMutableArray array];
    for (PHAsset * asset in _currentCollectionData) {
        //这个方法的回调是在主线程中回调的
        [[PHImageManager defaultManager] requestImageForAsset:asset
                                                   targetSize:CGSizeMake(targetSize, targetSize)
                                                  contentMode:PHImageContentModeAspectFill
                                                      options:options
                                                resultHandler:^(UIImage * _Nullable result, NSDictionary * _Nullable info) {
            [_imagesArray addObject:result];
        }];
    }

这里要特别说明一下targetSize,targetSize是你指定获取图片的大小,一定要按需设置,一般情况下照片都是很大的比如2000*3000,而屏幕的宽度只有375,图片的获取会占用系统资源。当同一时间大批量获取数据时(例如一个相册1000张图片),会耗费较多的时间,可能引发性能问题。

这里稍微展开下笔者的性能的优化问题

场景:相册有2000张图片,以collectionView进行展示,每行四个图片,以6s为例。整屏状态下同时会展示30个图片左右,当极速滑动时,不能有卡顿,且图片展示不发生错误。

笔者尝试过如下两种方法:

1.及时加载图片
2.统一加载图片

刚开始笔者用的第一种方法在Cell中进行加载,所有一切都很正常,但是当滑动稍快时,会有明显卡顿。遂采取了异步加载的方式,卡顿的确是解决了,可图片的展示又发生了问题(当图片获取到时,这时的Cell已经是另一个图片了)。再后来又加上了判断是否为当前图片,经过多次改进,还是会在极速滑动时能感觉到卡顿,陷入了困境。
代码越来越臃肿,越来越复杂,效果却差强人意。我打开了几个巨头的APP,他们的图片加载仿佛是进入选取界面的时候就已经加载好了,连iCloud的图片都没有显示加载的过程。当时觉得只有提前统一加载好了才能达到这种效果,总觉得不科学,直接加载上千张图片性能肯定会有问题。但笔者在第一条路已经绕晕了,也就尝试了下第二种方法,除了量特别大的情况下(数千张时),会有明显的加载过程,其他的时候感觉还不错(心里还是痒痒,有过来人还请不吝赐教)。

顺便列出一个方法,是将一个相册的图片预加载的,理论上可以提升加载图片的速度,具体效果未测:

    //load image to cache
    PHCachingImageManager * manager = [[PHCachingImageManager alloc] init];
    [manager stopCachingImagesForAllAssets];
    [manager startCachingImagesForAssets:_currentCollectionData
                              targetSize:CGSizeMake(targetSize, targetSize)
                             contentMode:PHImageContentModeAspectFill
                                 options:nil];

4 其他常用方法

取消指定的图片请求:

    [[PHImageManager defaultManager] cancelImageRequest:_requestID];

//_requestID为请求图片时的返回,如:
//- (PHImageRequestID)requestImageForAsset:targetSize:contentMode:options:resultHandler:

获取图片的详细信息

    PHContentEditingInputRequestOptions * editOptions = [[PHContentEditingInputRequestOptions alloc] init];
    editOptions.networkAccessAllowed = NO;
    PHContentEditingInputRequestID editID = [self requestContentEditingInputWithOptions:editOptions completionHandler:^(PHContentEditingInput * _Nullable contentEditingInput, NSDictionary * _Nonnull info) {
        //是否为iCloud资源
        //BOOL isCloud = [info[PHContentEditingInputResultIsInCloudKey] boolValue];
    }];

Demo: PJPhotoPicker

GitHub: https://github.com/BigBigPo/RJPhotoPicker
弱弱的问一句···这GIF怎么循环播放···

singleChoose.gif

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

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,019评论 4 62
  • github排名https://github.com/trending,github搜索:https://gith...
    小米君的demo阅读 4,538评论 2 38
  • 我是个不会感恩的人,不是没有神的恩典,而是不数算神的恩典就给忘了,就像今天在师母家小组数算神的恩典的时候,想起神在...
    周淑峰阅读 1,024评论 0 0
  • 人世间的幸福大多会以平庸收尾,但是这种平庸是多少人难以到达的境界和难以接受的现实,这种难不比成为一个大侠简单,甚...
    诗沐阳阅读 477评论 0 2
  • 主题:早起与反思是最佳拍档 舒适区的我,勇敢地回听了1/2/3讲,真的是精辟的干货!如果没有听到后面50多讲,前面...
    MarinaZhang阅读 175评论 0 2