iOS开发tips-PhotoKit
概述

PhotoKit应该是iOS 8 开始引入为了替代之前ALAssetsLibrary的相册资源访问的标准库,后者在iOS 9开始被弃用。当然相对于ALAssetsLibrary其扩展性更高,api使用起来也更加的强大,但这并非今天讨论的重点,这里主要讨论PhotoKit使用的一些技巧和容易踩的坑。
PHImageManager or Custom
访问相册资源常用的操作还是获取资源,比如获取一张相册的图片,可以通过PHImageManager.default().requestImage(xxx)一个回调就可以了,其实除了简单的图片拉取苹果建议大家使用一个自己维护的PHCachingImageManager(PHImageManager子类)单例,因为从名字可以看出它可以进行资源缓存(具体用法参见:PHCachingImageManager),这个在列表滑动过程中比较有用,因为可以使用startCachingImages(for:targetSize:contentMode:options:)预加载还未显示的资源,在不需要使用的时候调用stopCachingImages(for:targetSize:contentMode:options:)移除缓存,除此之外其实它更多的是使用父类的功能。
关于requestImage(for:targetSize:contentMode:options:resultHandler:)方法其实它的options是更容易让大家使用的时候经常迷糊的地方,因为iOS相册的照片很可能并不在本地而是在iCloud上面存储,本地只有缩略图:
- isNetworkAccessAllowed:是不是打开网络加载,对于iCloud优化的图片本地只有缩略图,需要了开启网络加载,默认是false,但是建议非特殊情况下不要关闭网络加载设为true
- isSynchronous:获取图片时是否同步获取,否则异步获取,非特殊情况下不建议使用同步获取会阻塞线程,默认为false
- version:使用资源的版本,例如说一个视频获取过程中是要获取编辑过的还是获取原始视频(因为iOS的相册编辑时可以恢复的,所以其实里面包含了编辑信息),默认current对于未编辑的则获取原始资源,编辑过则获取编辑过的资源。
- deliveryMode:字面意思资源交付模式,就是当发起一个请求后系统如何提供请求的资源(不过很可能这个资源并不在本地),默认是opportunistic同步调用只返回一种资源异步调用返回多种资源(例如不同尺寸的图片,这也就是说和上面的isSynchronous相关);highQualityFormat任何情况下只返回高质量的一种资源;fastFormat任何情况下只返回一种结果,它可能是低清图片(是不是低清晰度可以通过resultHandler结果中的info字段PHImageResultIsDegradedKey来判断是不是低清晰度)。
- resizeMode:重设尺寸模式,这个其实关系到方法中targetSize参数,默认是fast代表当本地是原图则返回原图,本地是缩略图则使用targetSize来获取一个最优图片,但是尺寸可能比targetSize要略大(注意如果targetSize是PHImageManagerMaximumSize则会拉取原图此属性此时没有意义直接忽略);none则返回原图大小;exact同fast但是返回的高清图。但是在异步情况下因为可能存在两种图片会稍有不同,下面分两种情况介绍。
isSynchronous和deliveryMode、resizeMode之间的关系(前提是开启网络加载isNetworkAccessAllowed = true):
isSynchronous = true同步加载:此时deliveryMode会被忽略掉,所以只要看resizeMode
- none:返回原图尺寸
- fast:原图为缩略图时使用targetSize优化,返回一个可能比targetSize稍大的图片
- exact:返回指定targetSize的高清图片
isSynchronous = false异步加载:此时要看deliveryMode和resizeMode两者的变化情况
deliveryMode = opportunistic
- none:先返回低清图片,再返回原图
- fast:先返回低清图片,再返回使用targetSize优化,可能比targetSize稍大的图片
- exact:先返回低清图片,再返回指定targetSize的高清图片
deliveryMode = highQualityFormat
- none:返回原图
- fast:返回使用targetSize优化,可能比targetSize稍大的图片
- exact:返回指定targetSize的高清图片
deliveryMode = fastFormat
- none:返回低清图片
- fast:返回低清图片
- exact:返回低清图片
对于是否返回两次结果总结起来对于返回结果只有异步请求在不设置deliveryMode或者deliveryMode = opportunistic 时会发送两次请求。
没错上面的情况如果了解不清楚很容易掉进坑里,一般的情况下只要打开网络,使用默认值即可。但是话说没有网络的情况下是什么情况呢?
无网络情况
上面说了那么多配置那么对于没有网络的情况呢?因为PHImageRequestOptions默认其实就是没有网络的。先看一下下面的请求:
let requestOption = PHImageRequestOptions()
requestOption.resizeMode = .exact
let scale:CGFloat = UIScreen.main.scale
let newSize = CGSize(width: 200.0, height: 200)
PHImageManager.default().requestImage(for: asset, targetSize: newSize, contentMode: .aspectFill, options: requestOption,resultHandler: completeHandler)
首先没有设置isNetworkAccessAllowed属性,那么默认值就是false,这个时候也就是没有任何网络请求,其次resizeMode = .exact也就是请求比较高清的图片。但是一个重要的问题是这个图片可能会是一个低清图片,甚至达不到一个200200的缩略图的预期,有可能特别模糊。
按照前面说的,deliveryMode 没有设置默认是opportunistic,isSynchronous*没有设置默认是false,在异步情况下是 首先这种情况下也会返回两个结果,打印info信息:
第一次:
[AnyHashable("PHImageResultIsDegradedKey"): 1, AnyHashable("PHImageResultRequestIDKey"): 1100]
第二次:
[AnyHashable("PHImageResultIsDegradedKey"): 0, AnyHashable("PHImageErrorKey"): Error Domain=NSCocoaErrorDomain Code=-1 "(null)", AnyHashable("PHImageResultIsInCloudKey"): 1, AnyHashable("PHImageResultRequestIDKey"): 1100]
可以看出第一次是低清图片,第二次是高清,但是此时出错了PHImageResultIsInCloudKey = 1,本地并没有高清图,但是因为无法访问网络去获取就报错了,抛出PHImageErrorKey信息,同时此时返回的image = nil,这样的结果就是只能看到低清图片。因此是否返回清晰照片除了上面说的,还和是否允许网络请求有直接关系。
综上来看除非特殊情况下,请打开网络请求使用异步请求,并且在合适的时机判断是否请求返回两次。
视频获取
对于视频资源的获取和图片还有些不同,因为使用PhotoKit上传视频的过程并不是直接获取到视频路径来做的,当然如果不熟悉这个过程很可能使用下面的方式:
let option = PHVideoRequestOptions()
option.isNetworkAccessAllowed = true
option.version = .current
option.deliveryMode = .highQualityFormat
PHImageManager.default().requestAVAsset(forVideo: phAsset, options: option) { (avAsset, _, _) in
if let avAsset = avAsset as? AVURLAsset {
let fileURL = avAsset.url
// upload by fileURL
}
}
这套代码遇到的问题很可能是iOS 10之前的系统上传失败,之后的基本还是可以传成功的,主要是因为这个url路径在低版本系统访问受限。
正确的姿势应该是先把视频导出到沙盒然后从本地上传,那么如何copy到本地呢?答案是使用PHAssetResourceManager,这个类出现的比较晚在iOS 9才引入的,就是为了方便资源管理,可以通过他将本地相册的资源写入沙盒然后上传沙盒的资源。
let resources = PHAssetResource.assetResources(for: phAsset)
let options = PHAssetResourceRequestOptions()
options.isNetworkAccessAllowed = true
PHAssetResourceManager.default().writeData(for: assetResource, toFile: videoURL, options: options, completionHandler: { (error) in
// upload by videoURL
})
但是这并非就万事大吉了,接下来的一个bug就是,通过这种方式如果上传用户已经编辑过的资源会发现它上传的是编辑之前的(比如说用户将60s的视频通过系统相册剪辑到50s,它上传的还是60s的),因为它相比较于PHImageManager请求可以设置PHVideoRequestOptions.version它缺少了这个信息,查找api也没有发现可以用的设置,所以这种方式还是不可取(如果有朋友知道怎么使用这种方式获取不同的version可以留言告诉我)。所以这时可以采用PHImageManager的另一个方法requestExportSession(xxx)将视频导出到沙盒然后上传,但是这么做的另一个小问题就是如果想要导出原始视频就不太可能了,只能尽可能拿到一个更高清晰度的视频。
let videoOption = PHVideoRequestOptions()
videoOption.version = PHVideoRequestOptionsVersion.current
videoOption.isNetworkAccessAllowed = true
PHImageManager.default().requestExportSession(forVideo: phAsset, options: videoOption, exportPreset: AVAssetExportPresetHighestQuality) { (exportSession, info) in
if let exportSession = exportSession {
exportSession.outputURL = videoURL
exportSession.outputFileType = AVFileType.mp4
exportSession.exportAsynchronously {
if exportSession.status == AVAssetExportSession.Status.completed {
// upload by videoURL
}
}
}
话说到了这里是不是就真的没事了?试着用下面的视频上传试试:
TestVideo.zip
exportSession.status应该会失败,当然如果你是iOS 13应该是可以的,但是如果是iOS 13以下在当前环境测试都是通不过的,这个视频查看meta信息也没有什么特殊的,但是这个是Android导出的一个视频,失败信息是:无法完成此操作。除此之外并没有有用的信息,初步猜测导出时应该是编解码出错了。
其实到了这里,用上面的方式应该就无法解决这个问题了,至少目前没有找到有效的办法,兜底策略就是失败后采用上面的第二种方式。
iOS开发tips-PhotoKit的更多相关文章
- iOS开发tips总结
tip 1 : 给UIImage添加毛玻璃效果 func blurImage(value:NSNumber) -> UIImage { let context = CIContext(opti ...
- iOS 开发 Tips
1.MVVM 的优点 MVVM 兼容 MVC,可以先创建一个简单的 View Model,再慢慢迁移. MVVM 使得 app 更容易测试,因为 View Model 部分不涉及 UI. MVVM 最 ...
- IOS开发-提升app性能的25条建议和技巧
前言 这篇文章介绍了作者开发工作中总结的25个iOS开发tips, 多年之前读过这篇文章.收益良多,基本每一个tips在我的应用开发过程中都使用过.今天把这篇文章又一次整理转发下,与大家一起学习,不论 ...
- iOS 开发之照片框架详解之二 —— PhotoKit 详解(下)
本文链接:http://kayosite.com/ios-development-and-detail-of-photo-framework-part-three.html 这里接着前文<iOS ...
- iOS 开发之照片框架详解之二 —— PhotoKit 详解(上)
转载自:http://kayosite.com/ios-development-and-detail-of-photo-framework-part-two.html 一. 概况 本文接着 iOS 开 ...
- iOS开发200个tips总结(一)
tip 1 : 给UIImage添加毛玻璃效果 func blurImage(value:NSNumber) -> UIImage { let context = CIContext(opti ...
- iOS开发:创建真机调试证书及描述文件
iOS开发:创建真机调试证书及描述文件 关于苹果iOS开发,笔者也是从小白过来的,经历过各种困难和坑,其中就有关于开发证书,生产证书,in_house证书,add_Hoc证书申请过程中的问题,以及上架 ...
- ios开发中的小技巧
在这里总结一些iOS开发中的小技巧,能大大方便我们的开发,持续更新. UITableView的Group样式下顶部空白处理 //分组列表头部空白处理 UIView *view = [[UIViewal ...
- fir.im Weekly - 94 个 iOS 开发资源推荐
距离 2016 年还有 17 个日夜,而你和回家只隔了一张 12306 验证码的距离,祝大家抢票顺利.本期 fir.im Weekly 收集了一些优秀的 GitHub 源码.开发工具和动画特效,希望对 ...
- fir.im Weekly - iOS开发中的Git流程
本期 fir.im Weekly 收集了微博上的热转资源,包含 Android.iOS 开发工具.源码等好用的轮子,还有一些 APP 设计的 Tips,希望对你有用. 精仿知乎日报 iOS 端 @我偏 ...
随机推荐
- 03 python学习笔记-文件操作(三)
本文内容主要包括以下方面: 1. 文件操作基本认识2. 只读(r, rb)3. 只写(w, wb)4. 追加(a, ab)5. r+读写6. w+写读7. a+写读(追加写读)8. 文件的修改 一.文 ...
- Java基础(二十二)集合(4)Set集合
Set集合为集类型.集是最简单的一种集合,存放于集中的对象不按特定方式排序,只是简单地把对象加入集合中.对集中存放的对象的访问和操作时通过对象的引用进行的,所以,在集中不能存放重复对象. Set接口实 ...
- unity 导出模型
不论unity是否运行,到处其中的模型方法: 1.如下图建立相关文件目录,并建立如下脚本 2.脚本代码 using UnityEngine; using UnityEditor; using Syst ...
- C语言I博客作业06
这个作业属于哪个课程 C语言程序设计I 这个作业要求在哪里 作业链接 我在这个课程的目标是 熟悉分支结构 这个作业在那个具体方面帮助我实现目标 可以更完整的编写程序及博客园 参考文献 [参考文献](h ...
- fenby C语言 P30
int a[5],*p=a; int a[5],*p=&a[0]; #include <stdio.h> int main(void){ int a[5],*p=a,i; for( ...
- 【控制系统数字仿真与CAD】实验二:结构图法数字仿真
一. 实验目的 1. 掌握结构图法仿真复杂控制系统的方法: 2. 掌握复杂系统联接矩阵W和输入联接矩阵W0的求解过程: 3. 掌握复杂系统的环节连接,矩阵A. B. C.D的求解过程: 4. 掌握MA ...
- 使用Typescript重构axios(九)——异常处理:基础版
0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...
- 【WPF on .NET Core 3.0】 Stylet演示项目 - 简易图书管理系统(3) - 使用Conductor切换页面
前两章中, 我们已经实现了这个图书管理系统的登录窗口, 并实施了完善的单元测试. 该是时候回过头来关注我们的主窗口了. 一个功能丰富的系统一般会有多个页面, 我们图书管理系统虽然是"简易&q ...
- ISCONF Redis is configured to save RDB snapshots
MISCONF Redis is configured to save RDB snapshots redis报错: (error) MISCONF Redis is configured to sa ...
- 九:写了一下红帽免费的centos6的安装步骤
linux centos 6安装方法 前提需要: 1, centos6的镜像文件 2,VMware 提前安装 注:获取镜像 阿里开源系统,此处可下载其他的 1.Ubuntu 2.Susa 3.Cent ...