介绍每天用iPhone拍摄的照片比用任何相机拍摄的照片都多。iOS设备上的显示屏每年都在变得越来越好,而在iPad出现并且没有Retina显示屏之前的日子里,大屏幕的杀手级功能之一就是能够展示用户照片和浏览器照片库.自从相机成为iPhone最重要、最独特的功能以来,对能够管理和编辑用户照片库中珍贵照片的应用程序和工具的需求就一直很大。直到2014年夏天,开发人员只能使用AssetsLibrary框架来访问不断增长的用户照片库。多年来,相机应用和照片应用发生了重大变化,增加了许多新功能,包括按时刻整理照片的方式。但与此同时,AssetsLibrary框架并没有跟上步伐。随着iOS8的出现,Apple为我们提供了一个现代框架-PhotoKit,它比AssetsLibrary性能更好,并且具有允许应用程序和设备照片库无缝工作的功能。总结我们将从框架的对象模型的概览开始:实体和实体之间的关系、检索实体的实例以及处理获取的结果。此外,我们的讲解还会涉及到一些资源元数据,这些资源元数据在使用AssetsLibrary时还没有对开发者开放。然后我们将讨论从资源加载图像数据:过程本身、过多的可用选项、一些陷阱和边缘情况。***,我们将讨论观察外部贡献者对照片库的更改,并学习如何为我们自己的修订创建和提交更改。PhotoKit对象模型PhotoKit定义了一个实体图,它对应于在系统的照片应用程序中呈现给用户的模型对象。这些照片实体是轻量级的不可变对象。所有的PhotoKit对象都继承自PHObject抽象基类,其公共接口只提供了一个localIdentifier属性。PHAsset表示用户照片库中的单个资产,提供资产的元数据。资源组称为资源集合,由PHAssetCollection类表示。单个资源集合可以是照片库中的相册或时刻,或者特殊的“智能相册”。这个智能相册包括所有视频收藏、最近添加的项目、用户收藏夹、所有连拍照片等等。PHAssetCollection是PHCollection的子类。PHCollectionList表示一组PHCollection。因为它本身是一个PHCollection,一个集合列表可以包含其他集合列表,这允许复杂的集合继承。我们实际上可以在照片应用程序的Moments部分看到它:例如照片---Moments---Featured---Years。FetchPhotoEntityFetchvs.Enumeration熟悉AssetsLibrary框架的开发人员可能还记得AssetsLibrary可以使用一些特定的属性来查找所需的资产,其中之一必须枚举用户资产库以获得匹配的资产。不得不承认,虽然这个API提供了一些缩小搜索范围的方法,但效率仍然很低。与之形成鲜明对比的是,获取PhotoKit实体的实例。熟悉CoreData的人会觉得它在概念和描述上都比较接近PhotoKit。Get请求Get操作由上述实体的类方法实现。使用哪个类/方法取决于问题的范围以及您如何显示和遍历照片库。所有获取方法的名称都相似:classfuncfetchXXX(...,options:PHFetchOptions)->PHFetchResult。options参数为我们提供了一种过滤和排序结果的方法,类似于NSFetchRequest的predicate和sortDescriptors参数。获取结果您可能已经注意到这些获取操作不是异步的。它们返回一个PHFetchResult对象,并且可以使用类似NSArray的接口访问结果中的集合。它按需动态??加载内容并缓存最近请求的内容。此行为类似于设置了batchSize属性的NSFetchRequest返回的结果数组。对于PHFetchResult,没有办法使用参数来指定这种行为,但是官网文档保证“即使在处理大量返回结果时,仍然可以有最好的性能”。即使满足请求的图片库内容发生变化,get方法返回的PHFetchResult对象也不会自动更新。在接下来的章节中,我们将介绍如何观察返回的PHFetchResult对象的变化,以及如何处理更新内容。临时集合(TransientCollections)你可能会发现你已经设计了一个可以对资源集合进行操作的组件,并且你还希望它能够处理任意一组资源。PhotoKit通过临时资源集合让我们很容易做到这一点。您可以从PHAsset对象数组或包含资产的PHFetchResult对象创建资产的临时集合。创建是在PHAssetCollection的transientAssetCollectionWithAssets(...)和transientAssetCollectionWithFetchResult(...)工厂方法中完成的。通过这些方法创建的对象可以像其他PHAssetCollection对象一样使用。不过,这些合集并不会存储在用户的照片库中,自然也不会显示在照片应用中。与资源集合类似,您可以使用PHCollectionList中的transientCollectionListWithXXX(...)工厂方法来创建临时集合列表。当您想合并两个获取请求时,您会发现这非常有用。照片元数据正如文章开头提到的,PhotoKit提供了以前使用ALAssetsLibrary框架无法访问或难以访问的有关用户资产的额外元数据。HDR和全景图您可以使用照片资产的mediaSubtypes属性来验证资产库中的图像是在打开HDR的情况下拍摄的,还是使用相机应用程序的全景模式拍摄的。FavoriteandHiddenAssets要验证资产是否已被用户标记为收藏或隐藏,只需检查PHAsset实例的favorite和hidden属性。对于连拍模式的资源,如果其PHAsset的representBurst属性为true,则表示该资源是一系列连拍照片中的一张代表照片(用户按住快门按钮时拍摄多张照片)。它还有一个属性burstIdentifier,如果你想获取连拍中剩下的照片,你可以将这个值传递给fetchAssetsWithBurstIdentifier(...)方法来获取。用户可以连拍标记照片;此外,系统会自动使用各种启发式方法来标记用户可能选择的具有潜在代表性的照片。此元数据可通过PHAsset的burstSelectionTypes属性访问。此属性是三个常量的位掩码:.UserPick用于用户手动标记的资源,.AutoPick用于用户可能标记的潜在资源,.None用于未标记的资源。此屏幕截图显示了照片应用程序如何自动标记用户可能在连拍照片中标记的潜在资源。照片加载在过去几年处理用户照片库的过程中,开发人员创造了成百上千个小技巧来提高照片加载和显示的效率。这些技巧处理请求分派和取消、图像大小调整和裁剪、缓存等。PhotoKit提供了一个类,它使用更方便、更现代的API来完成所有这些工作:PHImageManager。请求图像图像请求是通过requestImageForAsset(...)方法发送的。此方法接受一个PHAsset,它可以设置返回图像的大小和图像的其他可选选项(通过PHImageRequestOptions参数对象设置),以及结果回调(结果处理程序)。当不再需要请求的数据时,可以使用该方法的返回值取消请求。图像尺寸和裁剪奇怪的是,定义返回图像尺寸和裁剪的参数分布在两个地方。参数targetSize和contentMode将直接传递到requestImageForAsset(...)方法中。这个contentMode类似于UIView的contentMode参数,决定了照片是按比例缩放还是按比例填充的方式放在目标尺寸中。注意:如果没有调整大小或裁剪,则方法参数为PHImageManagerMaximumSize和PHImageContentMode.Default。此外,PHImageRequestOptions提供了确定图像管理器应如何调整图像大小的方法。resizeMode属性可以设置为.Exact(返回的图像必须与目标大小匹配)、.Fast(比.Exact更有效,但返回的图像可能与目标的大小不同)或.None。另外值得一提的是,normalizedCroppingMode属性让我们可以确定图像管理器应该如何裁剪图像。注意:如果设置了normalizedcroppingMode的值,那么resizeMode需要设置为.Exact。请求交付和进度默认情况下,如果图像管理器决定使用***策略,它会先交付图像的较低质量版本,然后再将高质量版本交付给您。您可以使用deliveryMode属性控制此行为;上述默认行为的值为.Opportunistic。如果您只想要高质量的图像并且可以接受更长的加载时间,则将属性设置为.HighQualityFormat。如果你想要更快的加载速度,但可以牺牲一点图像质量,那么将属性设置为.FastFormat。您可以使用PHImageRequestOptions的同步属性来使requestImage...系列方法同步。注意:当synchronous设置为true时,deliveryMode属性将被忽略并被视为.HighQualityFormat。设置这些参数时,请务必考虑您的某些用户可能已打开iCloud照片图库。PhotoKit的API不一定区分设备上的照片和iCloud上的照片——它们都使用相同的requestImage方法加载。这意味着任何图像请求都可能是蜂窝网络上非常慢的网络请求。当您要使用.HighQualityFormat或发出同步请求时,请记住这一点。注意:如果要确保请求不通过网络,请将networkAccessAllowed设置为false。另一个与iCloud相关的属性是progressHandler。您可以将其作为PHAssetImageProgressHandler的一个块,当从iCloud下载照片时,图像管理器会自动调用它。资源版PhotoKit允许应用程序对照片进行无损修改。对于编辑后的照片,系统会单独保存一份原照片和调整数据,供申请使用。使用图像管理器检索资源时,您可以指定应通过结果处理程序传送哪个版本的图像资源。这可以通过设置版本属性来完成:.Current将提供经过所有调整和修改的图像;.Unadjusted将提供未应用任何修改的图像;.Original将以其原始的、最高质量的格式提供图像(例如,RAW格式的数据。当属性设置为.Unadjusted时,将提供JPEG)。您可以在SamDavies的文章《照片扩展》的框架中阅读更多相关信息。结果回调(结果处理程序)结果回调是一个包含UIImage变量和信息字典作为参数的块。根据请求的参数和选项,图像管理器可以在请求的生命周期内多次调用它。信息字典提供有关请求当前状态的信息,例如是否必须从iCloud请求图像(如果您最初将networkAccessAllowed设置为false,则必须重新请求图像)-PHImageResultIsInCloudKey。当前交付的UIImage是否为最终结果的低质量格式。下载高质量图像时,这允许您首先向用户显示预览图像-PHImageResultIsDegradedKey。请求ID(可以很容易取消),以及请求是否已被取消-PHImageResultRequestIDKey和PHImageCancelledKey。如果没有向结果处理程序提供图像,字典中也会出现错误消息-PHImageErrorKey。这些值允许您更新您的UI以通知用户,连同上面讨论的progressHandler,以指示他们的加载状态。缓存当图像即将显示在屏幕上时,例如在滚动集合视图中显示大量资源图像的缩略图时,将一些图像预加载到内存中有时很有用。PhotoKit提供了PHImageManager的一个子类来处理这种特定的使用场景——PHImageCachingManager。PHImageCachingManager提供了一个关键方法——startCachingImagesForAssets(...)。您传入一组PHAssets、一些请求参数和一些在请求单个图像时将使用的可选选项。此外,还有一些方法允许您通知缓存管理器停止缓存特定资产列表,并停止缓存所有图像。allowsCachingHighQualityImages属性允许您指定图像管理器是否应准备高质量图像。该属性默认为true,在缓存简短且不变的资源列表时效果很好。但是如果想在collectionview上快速滑动的时候做缓存,最好设置为false。注意:根据我的经验,当用户在具有大量资源的集合视图上极快地滚动时,使用缓存管理器会损害滚动性能。为这个特定的使用场景定制一个缓存行为是极其重要的。缓存窗口的大小、移动缓存窗口的时间和频率、allowsCachingHighQualityImages属性的值-这些参数都经过仔细调整和测试,以在目标硬件上的真实照片库上进行性能测试。更进一步,您可以考虑根据用户行为动态设置这些参数。请求图片数据***,除了请求普通的UIImage,PHImageManager还提供了另一种方法,可以返回NSData对象类型的资源数据,包括其通用类型标识和图片的显示方向。此方法返回有关此资源的最多信息。事情发生了变化我们已经讨论了为用户照片库中的资产请求元数据,但到目前为止我们还没有提到如何更新我们获取的数据。照片库本质上是一大堆可变状态,而***部分提到的照片实体是不可变对象。PhotoKit可让您收到有关照片库更改的所有信息,以正确更新缓存状态。观察变化首先需要通过共享的PHPhotoLibrary对象用registerChangeObserver(...)方法注册一个变化观察者(这个观察者必须符合PHPhotoLibraryChangeObserver协议)。每当另一个应用程序或用户在照片库中进行更改影响您在更改之前获取的任何资源或资源集合时,更改观察者的photoLibraryDidChange(...)方法将被调用。此方法只接受一个PHChange类型的参数,您可以使用它来验证更改是否与您感兴趣的检索对象相关联。更新获取的结果PHChange提供了几种方法,允许您通过传入任何PHObject或PHFetchResult来跟踪更改你感兴趣。这些方法是changeDetailsForObject(...)和changeDetailsForFetchResult(...)。如果没有任何变化,这些方法返回nil,否则您可以在PHObjectChangeDetails或PHFetchResultChangeDetails对象的帮助下观察变化。PHObjectChangeDetails提供了对最新照片实体对象的引用,以及告诉你对象的图像数据是否曾经改变过、对象是否曾经被删除过的布尔值。PHFetchResultChangeDetails封装了有关应用于您从上一次提取中获得的PHFetchResult的更改的信息。PHFetchResultChangeDetails旨在尽可能轻松地更新CollectionView或TableView。它的属性精确映射到您使用典型CollectionView更新处理程序所需的信息。请注意,要正确更新UITableView/UICollectionView,您必须以正确的顺序处理更改,即:RICE-removedIndexes、insertedIndexes、changedIndexes、enumerateMovesWithBlock(如果hasMoves为真)。此外,PHFetchResultChangeDetails的hasIncrementalChanges属性可以设置为false,这意味着旧的fetch结果应该全部替换为新值。在这种情况下,您应该调用UITableView/UICollectionView的reloadData。注意:没有必要以集中方式处理更改。如果您的应用程序中有多个组件需要处理照片实体,那么它们都应该有自己的PHPhotoLibraryChangeObserver。然后,组件可以自行查询PHChange对象,以检测它们是否(以及如何)需要更新自己的状态。随风而变现在我们知道如何观察用户和其他应用程序引起的变化,让我们尝试自己做出改变。更改现有对象使用PhotoKit在照片库中进行更改。归根结底,你首先创建一个链接到某个资源或资源集合的更改请求对象,然后设置请求对象的相关属性或调用适当的方法来描述你想要的内容。提交更改。这必须在通过performChanges(...)方法提交给共享PHPhotoLibrary的块中完成。注意:您需要准备好处理performChanges方法的完成块中的失败。尽管处理可由多个参与者(例如您的应用程序、用户、其他应用程序、照片扩展等)更改的状态,但这种方法提供了安全性并且相对易于使用。要修改资源,需要创建PHAssetChangeRequest。然后您可以修改创建日期、资源位置、是否隐藏资源、是否将资源作为用户收藏等。另外,您可以从用户的库中删除资源。同样,要修改资源集合或集合列表,您需要创建一个PHAssetCollectionChangeRequest或PHCollectionListChangeRequest对象。然后您可以修改集合标题、添加或删除集合成员,或者完全删除集合。在您的更改提交到用户的照片库之前,系统会向用户显示一个明确的权限警告框。创建新对象创建新资源类似于修改现有资源。只需使用creationRequestForAssetFromXXX(...)工厂方法创建更改请求,传入资产图像数据(或URL)。如果您需要对新创建的资产进行其他修改,您可以使用创建更改请求的placeholderForCreatedAsset属性。它将返回一个可用的占位符来代替“真正的”PHAsset引用。结论我已经介绍了PhotoKit的基础知识,但还有很多东西有待发现。您可以通过查看示例中各处的代码、观看WWDC会议视频、深入挖掘并编写您自己的代码来了解更多信息!PhotoKit为iOS开发者开辟了一个充满可能性的新世界,在未来的岁月里,我们肯定会看到更多的创意和伟大的产品建立在这个基础上。
