iOS 开发实践:iOS照片API的那些坑

作者简介:keyishen(沈珂轶) 天天P图 iOS 工程师

在和图片打交道的那些日子里,遇到过不少图片相关的诡异问题。 在这里不会具体对照片API做介绍,而只会对其中的一些坑做一些总结。

1.不要完全相信系统API

当我们的程序有crash,但是通常我们的crash上报系统会上报自己app的crashlog,例如以下crashlog:

------------------------------------------------------------------------------------------------

由于是很偶现的crash,反复检查代码也没有发现什么疑点。还好在测试手机上后来发现了同一时间的另一个crashlog,如下:

------------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------------

原来在同一时间,系统负责AssetsLibrary功能的进程/System/Library/Frameworks/AssetsLibrary.framework/Support/assetsd crash了。从而导致了-[ALAsset valueForProperty:] 的调用始终卡在了那里。

系统的图片相关的操作主要是通过assetsd进程来实现的。

在对系统相册做一系列复杂操作后,有时会把系统assetsd进程搞挂,如果这时再回到app内调用Photos相关的API,就会出现异常现象,比如卡住,或者crash。这时可以看看手机里同一时刻之前是否有系统asset进程的crash堆栈。

我们目前的crash上报系统,往往无法定位到这张情况的问题。对于这种需要查看关联crash的情况就需要对系统的API也要合理地质疑。

2.不推荐自己写选图控件

对于选图没有太高要求的app,建议使用系统的选图控件UIImagePickerController,这样开发快捷便利,但是在我看来最大的优点在于这样做未来的维护成本会很小,尤其是可以在未来几乎第一时间享受到系统选图控件的新功能和新特性。

当然缺点是可调整的东西少,无法做个性化的定制,例如,不能控制UI,也很难做多图选择的扩展。

不过最不推荐的是在UIImagePickerController上面做UI的修改,这样虽然能够满足一时需求,但是却是为未来埋下了不少隐患,维护成本很高。

在iOS 10,如果用UIImagePickerController的话,还需要规避一个系统API会crash的坑。

在推出了3D touch之后,系统的UIImagePickerController在长按图片时会有偶现的crash。建议通过在UICollectionViewController的category里增加2个消息处理,来规避这个问题。具体增加的category如下:

3.支持的最低系统版本

苹果官方推荐始终只支持最新的2个大系统,就今天而言(2018.7)理论上我们应该只用支持iOS 10和iOS 11。然而国内的大环境,使得我们通常还需要从iOS 7,或者iOS 8开始支持。

对于图片类App来说,有一条很重要的分水岭,那就是iOS 8.1。

iOS 8开启了Photos.framework的新时代,而iOS 7及以下开发者只能使用AssetsLibrary的API。

然而可能是由于iOS 8推出匆忙,在iOS 8.0.x系统上,PHAsset的fetchAssetsWithMediaType: 和 fetchAssetsWithOptions:方法会返回iTunes同步的照片,以及iCloud照片流上的照片,所以如果你的照片被传到照片流上去后,通过这两个API返回的相册列表里会有两份相同的照片。

好在在iOS 8.1上苹果修改了这一API的行为,不再返回iTunes照片,以及照片流照片,使得Photos.framework从整个版本开始才真正意义上是可用的了。

对于小于iOS 8.1的系统都需要同时AssetsLibrary和Photos.framework两套图片API,对于非图片重度的app来说工作量不小。所以,推荐直接从iOS 9开始支持,如果不行的话,推荐至少从iOS 8.1开始支持(当然更合理的是从8.4开始支持,这样升级不到iOS 9的手机也有机会使用上)。

4.iCloud的坑

如果使用Photos.framework,那避不开的问题就是要支持iCloud上的照片。

为了推广苹果自己的iCloud的服务,并且拯救那些16GB手机的空间,苹果会将照片上传到iCloud,并且在本地只保存一份低清的照片。

4.1判断是否在iCloud上

我们APP在选图时需要判断本地是否存有原图。

Photos.framework提供了requestImageDataForAsset来获取图片的info。

Info的内容大致是这样的:

Printing description of info:

{

    PHImageResultDeliveredImageFormatKey = 9999;

    PHImageResultIsDegradedKey = 0;

    PHImageResultIsInCloudKey = 1;

    PHImageResultIsPlaceholderKey = 0;

    PHImageResultRequestIDKey = 54;

    PHImageResultWantedImageFormatKey = 9999;

}

理论上,info中的PHImageResultIsInCloudKey字段会告诉APP图片是否在iCloud上。

然而实际测试下来,该系统API有一个坑。在以上option的设置下,即使刚刚成功下载了这张图片,返回的info还是PHImageResultIsInCloudKey=1。

所以如果自己成功下载过图片后,还需要自己另外记忆下载的状态,在一定时间内,如果刚刚成功下载了图片,就应当要无视PHImageResultIsInCloudKey字段的状态,因为该字段的更新有滞后。

4.2判断iCloud API的卡死

在iOS 10系统上,还有一个新的坑,那就是用requestImageDataForAsset这个API会有一定概率出现永远不执行回调。

根据对在线用户的性能监控,我们发现这甚至是造成我们app卡顿的最大原因。通过对场景的重现我们发现,当这个API出现不回调bug时,各大主流app也几乎无一幸免,基本都卡在了那里。

所以为了解决这个问题,在调用requestImageDataForAsset的时候切忌放在主线程同步地做,并且需要给它一个超时时间,不让它无限制的执行。

5.正确获取缩略图

- (PHImageRequestID)requestImageForAsset:(PHAsset *)asset targetSize:(CGSize)targetSize contentMode:(PHImageContentMode)contentMode options:(nullable PHImageRequestOptions *)options resultHandler:(void (^)(UIImage *__nullable result, NSDictionary *__nullable info))resultHandler;

通过以上API,我们可以获取到各种尺寸的图片。虽然灵活性比之前AssetsLibrary的获取缩略图API高很多,但是方便程度差了不少,更多的灵活性带了的问题也不少。

这里涉及到一个系统API的坑requestImageForAsset这个API在某些targetSize下返回为空。

例如,在测试设备iPad Air(iOS 9.3.1)上,

如果targetSize=CGSizeMake(x, x),

当contentMode=PHImageContentModeAspectFill时,

且1<=x<=64 or 81<=x<=257时,

resultHandler返回的result image为nil。

当contentMode=PHImageContentModeAspectFit时,

且121<=x<=385时,

resultHandler返回的result image为nil。

在调用时需要多试试各个系统以及机型的适配性,尽量避开这些取值范围。

6.删除图片的API

在低于iOS 8的系统上,AssetsLibrary没有明确地提供删除图片的接口。

但事实上很多图片类APP通过修改图片的接口起到删除图片的作用,即通过ALAsset的接口直接删除图片。

但是当base sdk到了iOS 10之后,我们发现之前能用的接口现在在iOS 8.1及以上系统,会出现成功回调不执行的问题。

解决方法也很简单,就是直接使用Photos.framework提供的接口来删除图片:

附录:

https://objccn.io/issue-21-4/

http://stackoverflow.com/questions/25883005/avoiding-duplicates-when-getting-pictures-with-phasset

文章后记: 天天P图是由腾讯公司开发的业内领先的图像处理,相机美拍的APP。欢迎扫码或搜索关注我们的微信公众号:“天天P图攻城狮”,那上面将陆续公开分享我们的技术实践,期待一起交流学习!

加入我们: 天天P图技术团队长期招聘: (1) 图像处理算法工程师 (2) Android / iOS 开发工程师 期待对我们感兴趣或者有推荐的技术牛人加入我们(base 上海)!联系方式:ttpic_dev@qq.com

原文发布于微信公众号 - 天天P图攻城狮(ttpic_dev)

原文发表时间:2018-07-09

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏日常学python

同学利用Python爬虫制作王者荣耀出装助手,引来了隔壁班的女生!

暑假回家,”小皇帝”般的生活持续了几天,头几天还挺舒服,闲久了顿时觉得好没意思。眼看着10天的假期就要结束,曾信誓旦旦地说要回家学习,可拿回家的两本书至今一页未...

17420
来自专栏施炯的IoT开发专栏

《101 Windows Phone 7 Apps》读书笔记-Subservient Cat

课程内容 Ø Playing Video Ø MediaElement     Subservient Cat是一个“虚拟宠物”的应用程序。与大多数猫不一...

20390
来自专栏用户2442861的专栏

前端开发介绍(包含调试什么的)

http://www.cnblogs.com/jikey/p/4259360.html

38620
来自专栏携程技术中心

干货 | Qreact,去哪儿网的迷你react方案

作者简介 钟钦成,网名司徒正美,著名的JavaScript专家,去哪儿网前端架构师。在GITHUB拥有复数个著名的轮子,著有《javascript框架设计》一书...

41480
来自专栏CSDN技术头条

前端请进:玩转Angular 注射器

? 2009 年 AngularJS 第一个把“依赖注入”机制引入到了前端开发中,开创了用后端设计思想大规模入侵前端领域的先河。 如果没有深入使用过 Spri...

45470
来自专栏编程微刊

细数那些年我用过的前端开发工具

37220
来自专栏编程微刊

提高工作效率的几个小技巧

16940
来自专栏ATYUN订阅号

Medium网友开发了一款应用程序 让学习算法和数据结构变得更有趣

Medium网友Peter Weinberg开发了一款名叫CS-Playground-React的应用程序,可以使大家更有意思、也更加轻松地学习算法和数据结构。...

48650
来自专栏程序员的知识天地

前后端分离后的前端时代,使用前端技术能做哪些事?

什么是前后端分离,要区分前端和后端,需要有个明确的界限。一般,用户可以直接看到的东西,都是属于前端的范畴,除了前端之外都属于后端了。

39420
来自专栏工科狗和生物喵

微信小程序开发——跑步App+音乐播放

开篇语 好不容易,终于把所有的基础课程全部看完了!昨天,我很高兴地开始了看别人做的项目进行深度的学习。其实也说不上是项目吧,更多的像是一种给新手看的示例代码。然...

750120

扫码关注云+社区

领取腾讯云代金券