概述

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则返回原图大小;exactfast但是返回的高清图。但是在异步情况下因为可能存在两种图片会稍有不同,下面分两种情况介绍。

isSynchronousdeliveryModeresizeMode之间的关系(前提是开启网络加载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 没有设置默认是
opportunisticisSynchronous*没有设置默认是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的更多相关文章

  1. iOS开发tips总结

    tip 1 :  给UIImage添加毛玻璃效果 func blurImage(value:NSNumber) -> UIImage { let context = CIContext(opti ...

  2. iOS 开发 Tips

    1.MVVM 的优点 MVVM 兼容 MVC,可以先创建一个简单的 View Model,再慢慢迁移. MVVM 使得 app 更容易测试,因为 View Model 部分不涉及 UI. MVVM 最 ...

  3. IOS开发-提升app性能的25条建议和技巧

    前言 这篇文章介绍了作者开发工作中总结的25个iOS开发tips, 多年之前读过这篇文章.收益良多,基本每一个tips在我的应用开发过程中都使用过.今天把这篇文章又一次整理转发下,与大家一起学习,不论 ...

  4. iOS 开发之照片框架详解之二 —— PhotoKit 详解(下)

    本文链接:http://kayosite.com/ios-development-and-detail-of-photo-framework-part-three.html 这里接着前文<iOS ...

  5. iOS 开发之照片框架详解之二 —— PhotoKit 详解(上)

    转载自:http://kayosite.com/ios-development-and-detail-of-photo-framework-part-two.html 一. 概况 本文接着 iOS 开 ...

  6. iOS开发200个tips总结(一)

    tip 1 :  给UIImage添加毛玻璃效果 func blurImage(value:NSNumber) -> UIImage { let context = CIContext(opti ...

  7. iOS开发:创建真机调试证书及描述文件

    iOS开发:创建真机调试证书及描述文件 关于苹果iOS开发,笔者也是从小白过来的,经历过各种困难和坑,其中就有关于开发证书,生产证书,in_house证书,add_Hoc证书申请过程中的问题,以及上架 ...

  8. ios开发中的小技巧

    在这里总结一些iOS开发中的小技巧,能大大方便我们的开发,持续更新. UITableView的Group样式下顶部空白处理 //分组列表头部空白处理 UIView *view = [[UIViewal ...

  9. fir.im Weekly - 94 个 iOS 开发资源推荐

    距离 2016 年还有 17 个日夜,而你和回家只隔了一张 12306 验证码的距离,祝大家抢票顺利.本期 fir.im Weekly 收集了一些优秀的 GitHub 源码.开发工具和动画特效,希望对 ...

  10. fir.im Weekly - iOS开发中的Git流程

    本期 fir.im Weekly 收集了微博上的热转资源,包含 Android.iOS 开发工具.源码等好用的轮子,还有一些 APP 设计的 Tips,希望对你有用. 精仿知乎日报 iOS 端 @我偏 ...

随机推荐

  1. Python之路(第四十六篇)多种方法实现python线程池(threadpool模块\multiprocessing.dummy模块\concurrent.futures模块)

    一.线程池 很久(python2.6)之前python没有官方的线程池模块,只有第三方的threadpool模块, 之后再python2.6加入了multiprocessing.dummy 作为可以使 ...

  2. Apache 4.x HttpClient

    public static Map callRequest(String requestUrl, Method method, Map<String, String> data) thro ...

  3. JVM(3) 垃圾收集器与内存分配策略

    一.垃圾收集的概念 在Java虚拟机运行时数据区中程序计数器.虚拟机栈和本地方法栈3个区域随线程而生,随线程而灭:栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈操作,每一个栈帧中分配多少内 ...

  4. spring-boot-plus是易于使用,快速,高效,功能丰富,开源的spring boot 脚手架.

    Everyone can develop projects independently, quickly and efficiently! spring-boot-plus是一套集成spring bo ...

  5. jwt token

    1 ,session 认证机制: ,用户登录,传递用户名和密码给客户端 ,服务器进行用户名和密码的校验,如果校验成功,将用户保存到session ,将sessionid通过cookie返回给客服端,客 ...

  6. CSPS模拟 46

    勿忘国耻. 由于重新评测我看到了不是很真实的一幕 紧接着是更不真实的一幕 就在虚假形象快要建立完成的时候 它由于来自东方的神秘力量倒塌了 被两个学校的大佬爆踩了(捂脸 T1 无脑背包? 考试时想1h想 ...

  7. 【IOS开发—视图控制器】

    一.UIViewController 视图控制器是UIViewController类或者其子类对象,每个视图控制器都负责管理一个视图层次结构.在UIViewController中有一个重要的UIVie ...

  8. 爬虫学习--常用的正则表达式 Day3

    在做爬虫经常遇到需要用正则校验数据时候,往往是在网上去找很久,结果找来的还是不很符合要求.所以我最近把开发中常用的一些正则表达式整理了一下,给自己留个底,也给朋友们做个参考. 一.校验数字的表达式 1 ...

  9. .net core Json字符串的序列化和反序列化通用类源码,并模拟了10万数据对比DataContractJsonSerializer和Newtonsoft性能

    我们在开发中Json传输数据日益普遍,有很多关于Json字符串的序列化和反序列化的文章大多都告诉你怎么用,但是却不会告诉你用什么更高效.因为有太多选择,人们往往会陷入选择难题. 相比.NET Fram ...

  10. 你了解MySQL的加锁规则吗?

    注:加锁规则指的是next-key lock,如果还不了解next-key lock,请阅读上一篇博客 加锁规则可以概括为:两个原则.两个优化和一个bug: 原则1:加锁的基本单位是next-key ...