因为项目的需求,要捕获相册中的视频与图片,之前也对之尚未做过什么探讨,仅知道iOS8.0后,苹果推出了新的相册管理的包,那么正好,现在可以看看这个框架了,也知道AlAssetLibrary,使用的话倒是没有详细应用过,因此也不与PhotoKit进行对比了。
![](https://img.haomeiwen.com/i1408682/b30fd28e39892b80.gif)
![](https://img.haomeiwen.com/i1408682/61c1b8e0c592785f.png)
类的介绍:
//PHCachingImageManager(PHImageManager的抽象) 处理图像的整个加载过程的缓存要加载大量资源的缩略图时可以使用该类的startCachingImage...预先将图像加载到内存中 ,使用时注意size要一致
![](https://img.haomeiwen.com/i1408682/d6c105a2cc423221.png)
![](https://img.haomeiwen.com/i1408682/a0b87e6a96574899.png)
先挑明自己踩到的坑..
1、 首次调用相册权限的为难:仅会获取相应的权限提示 而不会响应方法
//每次访问相册都会调用这个handler 检查改app的授权情况
//PHPhotoLibrary
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
if (status == PHAuthorizationStatusAuthorized) {
//code
}
}];
2、获取所有图片(注意不能在胶卷中获取图片,因为胶卷中的图片包含了video的显示图)
[PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:nil];//这样获取
也可以使用PHFetchOptions中的谓词过滤获取
PHFetchOptions *fetchResoultOption = [[PHFetchOptions alloc] init];
fetchResoultOption.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:false]];
fetchResoultOption.predicate = [NSPredicate predicateWithFormat:@"mediaType = %d",PHAssetMediaTypeImage];
3、使用PHImageManager请求时的回调同步or异步时、block回调次数的问题
4、回调得出的图片size的问题: 由3个参数决定
在ShowAlbumViewController 中观察
在PHImageContentModeAspectFill 下 图片size 有一个分水岭 {125,125} {126,126}
当imageOptions.resizeMode = PHImageRequestOptionsResizeModeExact;
时: 设置size 小于{125,125}时,你得到的图片size 将会是设置的1/2
而在PHImageContentModeAspectFit 分水岭 {120,120} {121,121}
5、回调中info字典key消失的问题:
简单地说,就是你如果是自定义的size,且返回的图片的size小于源图片的size 那么你将会得不到图片的相对路径等部分key
6、显示的相册名字不是中文:在info.plist 中设置
Localized resources can be mixed YES
本地资源的适配
7、This app has crashed because it attempted to access privacy-sensitive data
without a usage description.
The app's Info.plist must contain an NSPhotoLibraryUsageDescription key
with a string value explaining to the user how the app uses this data.
本app因未得到使用说明就直接企图访问私人数据而导致崩溃。
app想要访问这些私人数据就得在info.plist增加一个以NSPhotoLibraryUsageDescription为键,以字符串为值的键值对。=
一、先来一个简单的练习吧:获取本机系统所有的图片
在collectionView中
- (void)getAllPhotosFromAlbum {//配置简单 ,但是参数却是比价多且
self.options= [[PHImageRequestOptions alloc] init];//请求选项设置
self.options.resizeMode=PHImageRequestOptionsResizeModeExact;
//resizeMode 自定义设置图片的大小 枚举类型*
// PHImageRequestOptionsResizeMode:*
//PHImageRequestOptionsResizeModeNone = 0, //保持原size
//PHImageRequestOptionsResizeModeFast, //高效、但不保证图片的size为自定义size
//PHImageRequestOptionsResizeModeExact, //严格按照自定义size
self.options.synchronous=YES; //YES 一定是同步 NO不一定是异步
imageOptions.resizeMode = PHImageRequestOptionsResizeModeExact;
/*
PHImageRequestOptionsResizeModeNone // 不调整大小
PHImageRequestOptionsResizeModeFast // 由系统去安排,情况不定:有时你设置的size比较低,会根据你设的size,有时又会比
PHImageRequestOptionsResizeModeExact// 保证精确到自定义size :此处精确的前提得用PHImageContentModeAspectFill
*/
//simageOptions.version = PHImageRequestOptionsVersionCurrent;//版本 iOS8.0之后出的图片编辑extension,可以根据次枚举获取原图或者是经编辑过的图片,
/*PHImageRequestOptionsVersion:
PHImageRequestOptionsVersionCurrent = 0, //当前的(编辑过?经过编辑的图:原图)
PHImageRequestOptionsVersionUnadjusted, //经过编辑的图
PHImageRequestOptionsVersionOriginal //原始图片
*/
// imageOptions.networkAccessAllowed = YES;//用于开启iClould中下载图片
// imageOptions.progressHandler //iClould下载进度的回调
imageOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;//在imageOptions.synchronous = NO的情况下最终决定是否是异步
//容器类
(PHFetchResult *) self.assets= [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:nil];
//此处option是对获取得到对 Collection 的配置 我只是把它设为nil了 可以这样使用
/*
//例如按资源的创建时间进行一个排序
PHFetchOptions *options = [[PHFetchOptions alloc] init];
// NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:nil ascending:YES];
options.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"creationDate" ascending:YES]];
//其中:key是PHAsset类的属性 这是一个kvc
PHFetchResult *assetsFetchResults = [PHAsset fetchAssetsWithOptions:options];
*/
[self.containView.collectionView reloadData];
}
- (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView cellForItemAtIndexPath:(NSIndexPath*)indexPath {
AlbumCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:ALBUMCELLID forIndexPath:indexPath];
//cell.backgroundColor= [UIColor redColor];
CGSize size =CGSizeMake(50,50);//自定义image size变化情况颇为复杂 下面由说到
//返回一个 PHImageRequestID,在异步请求时可以根据这个ID去取消请求,同步就没办法了..
[[PHImageManager defaultManager] requestImageForAsset:self.assets[indexPath.row] targetSize:size contentMode:PHImageContentModeDefault options:self.options resultHandler:^(UIImage*_Nullable result,NSDictionary*_Nullable info) {
/*
最终产生图片的size是有 imageOptions.resizeMode(即PHImageRequestOptions) 以及 PHImageContentMode 决定的,当然也有我们设定的size
优先级而言
PHImageRequestOptions > PHImageContentMode
*/
//这个handler 并非在主线程上执行,所以如果有UI的更新操作就得手动添加到主线程中
// dispatch_async(dispatch_get_main_queue(), ^{ //update UI });
#pragma If -[PHImageRequestOptions isSynchronous] returns NO (or options is nil), resultHandler may be called 1 or more times. .........异步就这个回调会调用1或多次....
#pragma If -[PHImageRequestOptions isSynchronous] returns YES, resultHandler will be called exactly once同步就1次.
//一开始本来打算是利用数据吧所有的相片先存起来的.但是发现同步会卡UI 但是异步又会被回调多次,我的数组都变成double了...只好把任务方法方法cellforItem中
cell.photoImageView.contentMode = UIViewContentModeScaleAspectFit;
cell.photoImageView.image = result;
}];
return cell;
}
/*注意这个info字典 有时这个info甚至为null 慎用
里面的key是比较奇怪的
尽量不要用里面的key
因为这个key 会变动: 当我们最终获取到的图片的size的高/宽 没有一个达到能原有的图片size的高/宽时
部分key 会消失 如 PHImageFileSandboxExtensionTokenKey , PHImageFileURLKey
*/
/*
在PHImageContentModeAspectFill 下 图片size 有一个分水岭 {125,125} {126,126}
当imageOptions.resizeMode = PHImageRequestOptionsResizeModeExact;
时: 设置size 小于{125,125}时,你得到的图片size 将会是设置的1/2
而在PHImageContentModeAspectFit 分水岭 {120,120} {121,121}
至于为什么会这样???- - 可能苹果考虑性能吧
*/
注意到上面这个resultHandler,返回一个UIImage,以及一个NSDictionary,图片就不多说了,关键是这个info实在是诡异...
这就引起了我的关注:size和key的关系
围绕着size的大小这个key竟然是不一样的............
我把图也打印出来了// 条件: size = {300,300}
![](https://img.haomeiwen.com/i1408682/f2a542dfc3cd0776.png)
本来是猜测我们设定的这个size比原图小的时候,会产生一张新的图片,而这张图片本地资源是不存在的,所以少了部分的key,
但是现在就尴尬了,看第二个image的原图,分明也是比{300,300}小的,为什么也没了部分key了呢,不太懂....
大家不要误会,第一张图片的原图是肯定大于{300,300} 的,为什么显示不是{300,300},因为我使用处理图片的模式为PHImageContentModeAspectFit,所以显示下来的并非我们{300,300}而是保留原图的比例,大家可以动手去试一试
而且这个size其实是像素:
CGSizeMake(self.assets[indexPath.row].pixelWidth,self.assets[indexPath.row].pixelHeight);
或者设定一个肯定比原图大的size就能获取到原图了....当然,后者是不靠谱的...
或许是这个size出的问题吧,会导致如果我要根据这个PHimageFileKey 获取本地的图片的时候就因为某部分的image因为size的原因缺少这个key使得找不到这张图,使得我app出现bug....不过这个方法真的挺不安全的,建议是把返回的result 那张image 写进tmp里面 再使用吧。。。。
再者就是异步请求次数跟size的关系
当我的self.options.synchronous=NO;//就是一个异步请求的过程(后来发现其实异步请求并不由options的synchronous属性决定)
而这个PHImageRequestOptions 可以根据它的一个属性deliveryMode 去控制异步请求的次数
PHImageRequestOptionsDeliveryMode:
PHImageRequestOptionsDeliveryModeOpportunistic//根据我self.options.synchronous判断返回结果是一个抑或多个
PHImageRequestOptionsDeliveryModeHighQualityFormat //制定的同步返回一个结果,返回的图片质量是比我们设定的size会好一点(实际上与PHImageRequestOptions的resizeMode枚举相关)
PHImageRequestOptionsDeliveryModeFastFormat//仅返回一次,效率较高之余获得的图质量不太好
我这边配合的resizeMode模式为强制自定义的图片大小,即:
self.options.resizeMode=PHImageRequestOptionsResizeModeExact;//严格按照自定义图片大小的加载模式
如果不是这个枚举的话,返回的PHImageRequestOptionsDeliveryModeOpportunistic第一次返回的缩略图,以及PHImageRequestOptionsDeliveryModeFastFormat所返回的缩略图最小默认是{60,60},
PHImageRequestOptionsResizeModeExact则可以随意设置
值得注意的是,当我们选择的是PHImageRequestOptionsDeliveryModeOpportunistic时,返回的次数是跟size有关系的,你设定的的size 太小的话仅会回调一次,大的话,首次返回的是一个小的缩略图(这个缩略图最大是{60,60},具体值根据比例),二次回调才会得到我们想要的图片
总而言之,图片的真实size跟resizeMode和PHImageRequestOptionsDeliveryMode以及我们设定的size都有关联吧,具体应用的时候就要多试试几次,观察自己真实所需吧...
接下来的是我要找的是视频......问题又出现了,这个PhotoKit有提供查找视频的方式么...
然后我把这修改了一下。。。改成了找Video 的元数据 哈哈哈哈哈哈哈
self.assets= [PHAsset fetchAssetsWithMediaType:**PHAssetMediaTypeVideo** options:nil];
但我好像又踩上了自己挖的坑了...
![](https://img.haomeiwen.com/i1408682/6716a45341acf148.png)
全是图片....不,不是这样的。。。
在回头看看发现有PHImageManager 这个单例提供我们3个方法去找得到Vide 我则选取了其中一个通用的方法
PHFetchResult *assetsResult = [PHAssetfetch AssetsWithMediaType:PHAssetMediaType Videooptions:nil];
PHVideoRequestOptions *options2 = [[PHVideoRequestOptions alloc] init];
options2.deliveryMode=PHVideoRequestOptionsDeliveryModeAutomatic;
for(PHAsset *a in assetsResult) {
[[PHImageManager defaultManager] requestAVAssetForVideo:a options:options2 resultHandler:^(AVAsset*_Nullable asset,
AVAudioMix*_Nullable audioMix,NSDictionary*_Nullable info) {
NSLog(@"%@",info);
}];
}
找到你了....
不过你居然在。。。PHImageFileSandboxExtensionTokenKey。。。这个key里面
![](https://img.haomeiwen.com/i1408682/2724876c072103f7.png)
看来地址是要用要用截取的了,可以直接输入到finder就能找到这个视频,但是前面的字符串就得让我们手动去取其子字符串只需要拿到video的Path就可以了
很不安全,我真的怕这个key突然又因为什么原因而miss掉了........
然后我看了看asset 的类型 发现它的类型为AVURLAsset,有点意思,看看里面发现了一个是有个URL属性的!而且是asset的相对路径, 好像可以做点什么...
//video路径获取
if (asset && [asset isKindOfClass:[AVURLAsset class]] && [NSString stringWithFormat:@"%@",((AVURLAsset *)asset).URL].length > 0) {
NSString *videoURLStr = [NSString stringWithFormat:@"%@",((AVURLAsset *)asset).URL];
videoPath = ((AVURLAsset*)asset).URL.path;
}
二、根据实际需求的部分获取资源
怎么个实际法呢,有时候你对着一大堆图片,然后找啊找,调啊调,还真的不是什么好方法...为什么就不能在我分好的类别中选取呢!!!!
好的,就下来就说这种情况,大家在开头都一定看到那幅,资源与资源集合的一个关系图...
获取所有用户自定义的相册:1\2\3分别是我创建的相册,以下代码能捕获到,自己创建的的相册的内容
![](https://img.haomeiwen.com/i1408682/74c7cd26c07b795d.png)
捕获系统相册中的图片/video
![](https://img.haomeiwen.com/i1408682/6ab1be7e64615ecc.png)
![](https://img.haomeiwen.com/i1408682/689519182aaea988.png)
解释一下
PHFetchResult*smartAlbums = [PHAssetCollection fetchAssetCollectionsWithType:PHAssetCollectionTypeSmartAlbum subtype:PHAssetCollectionSubtypeAlbumRegular options:nil];
这个获取资源集合的方法 需要填写两个枚举的类型,大概翻译了下,有错误的大家可以提一下,我会非常感激的
![](https://img.haomeiwen.com/i1408682/185f4cc8f3493d03.png)
demo地址
参考文章:
1、照片框架:https://objccn.io/issue-21-4/#PhotoKit-Object-Model%5D(http://objccn.io/issue-21-4/#PhotoKit-Object-Model
2、iOS 开发之照片框架详解:http://kayosite.com/ios-development-and-detail-of-photo-framework.html
网友评论
}];
把这个方法放到 循环中 遍历 UIimage 添加到数组 作为数据源 这种方法现实吗
还有这个方法语法写错了,麻烦更新下,demo是对的,感谢分享
帧可以用AVAssetImageGenerator提取出来
// Typically in this case, resultHandler will be called asynchronously on the main thread with the requested results.
// However, if deliveryMode = PHImageRequestOptionsDeliveryModeOpportunistic, resultHandler may be called synchronously on the calling thread if any image data is immediately available. If the image data returned in this first pass is of insufficient quality, resultHandler will be called again, asychronously on the main thread at a later time with the "correct" results.
// If the request is cancelled, resultHandler may not be called at all.
// If -[PHImageRequestOptions isSynchronous] returns YES, resultHandler will be called exactly once, synchronously and on the calling thread. Synchronous requests cannot be cancelled.
// resultHandler for asynchronous requests, always called on main thread
if (asset && [asset isKindOfClass:[AVURLAsset class]] && [NSString stringWithFormat:@"%@",((AVURLAsset *)asset).URL].length > 0) {
NSString *videoURLStr = [NSString stringWithFormat:@"%@",((AVURLAsset *)asset).URL];
NSString *videoPath = [videoURLStr substringWithRange:NSMakeRange([videoURLStr rangeOfString:@"/"].location, videoURLStr.length -[videoURLStr rangeOfString:@"/"].location)];
}获取到视频的路径,再去读NSData *data = [NSData dataWithContentsOfFile:ideoPath];
得到的data为空,求解?难道不能读取系统相册的数据吗?
btw 如果要从很多视频文件中获取指定视频的url要用什么方法呢