概述

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. redis之PubSub

    前面我们讲了 Redis 消息队列的使用方法,但是没有提到 Redis 消息队列的不足之处,那就是它不支持消息的多播机制. 消息多播 消息多播允许生产者生产一次消息,中间件负责将消息复制到多个消息队列 ...

  2. OptimalSolution(6)--栈和队列

    一.设计一个有getMin功能的栈 题目:实现一个特殊的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作.pop.push.getMin操作的时间复杂度都是O(1). 思路:设计两个栈,一 ...

  3. class定义类 及 实现继承

    class 定义类 代码如下: class Student { constructor(name) { this.name = name; } sayHello() { console.log(&qu ...

  4. 【并发编程】Volatile原理和使用场景解析

    目录 一个简单列子 Java内存模型 缓存不一致问题 并发编程中的"三性" 使用volatile来解决共享变量可见性 volatile和指令重排(有序性) volatile和原子性 ...

  5. 【Java必修课】HashMap性能很好?问过我EnumMap没

    1 简介 我们知道Map只是一个接口,它有多种实现,Java中最常用的是HashMap了.而本文想讲述的是另一个实现:EnumMap.它是枚举类型的Map,要求它的Key值都必须是枚举型的. 2 创建 ...

  6. 《Effective Java》 读书笔记(八)避免使用Finalizer和Cleaner机制

    Finalizer和Cleaner并不等同于C++中的析构函数,是不确定多久会被调用的,甚至有时候可能不会被调用,因此除了作为一个安全网或者终止非关键的本地资源,不应该在Finalizer或Clean ...

  7. NOIP模拟 2

    大概就是考试的时候慌的一批,因为一道正解也没想出来,T1,T3只会暴搜,听见天皇在旁边的窃喜声本渣内心是崩溃的 会打暴搜的我先打了暴搜,大多数时间都用在第二题上,妄想自己能拿50多分- 最后半小时万念 ...

  8. 星空 题意转化,差分,状压DP

    好题(爆搜和puts("2")一个分(雾)),不得不说思维真的强. 首先发现区间翻转很难受,考虑用差分(异或满足可逆性),注意是从0到n+1 然后就转化题意,操作改为选取距离为L的 ...

  9. 百度艾尼(ERNIE)常见问题汇总及解答

    一.ERNIE安装配置类问题 Q1:最适合ERNIE2.0的PaddlePaddle版本是?A1:PaddlePaddle版本建议升级到1.5.0及以上版本. Q2:ERNIE可以在哪些系统上使用?A ...

  10. SQL Server 中 `JSON_MODIFY` 的使用

    SQL Server 中 JSON_MODIFY 的使用 Intro SQL Server 从 2016 开始支持了一些 JSON操作,最近的项目里也是好多地方直接用字段直接存成了 JSON,需要了解 ...