PhotoKit類組成:
PHAdjustmentData,繼承自NSObject,代表調整的數據.當用戶編輯了一個asset(相冊中的多媒體文件)、Photos框架會隨著修改后的圖像或視頻數據生成一個adjustmentData對象。這個對象提供了程序默認的初始數據可以用來重現恢復這個編輯過的asset。
PHAsset,繼承自PHObject。代表照片庫中的一個資源,通過 PHAsset 可以獲取和保存資源。
PHAssetChangeRequest,繼承自NSObject,用于創建,刪除,更改元數據或編輯照片資源內容的請求,以便在照片庫更改塊中使用。。
PHAssetCollectionChangeRequest,繼承自NSObject,創建,刪除或修改照片資產集合的請求,以便在照片庫更改塊中使用。。
PHAssetResourceCreationOptions,,繼承自NSObject,影響從基礎資源創建新Photos資產的一組選項。
PHAssetResource,繼承自NSObject,與照片庫中的照片,視頻或Live Photo資產相關聯的基礎數據資源。
PHAssetResourceRequestOptions,繼承自NSObject,影響您從資產資源管理器請求的基礎資產數據交付的一組選項。
PHAssetResourceManager,繼承自NSObject,提供訪問與Photos資產關聯的資源的基礎數據存儲的方法。
PHChange,繼承自NSObject,“照片”庫中發生的更改的說明。
PHCollection,繼承自PHObject。
PHAssetCollection,繼承自PHCollection。表示一個相冊或者一個時刻,或者是一個「智能相冊(系統提供的特定的一系列相冊,例如:最近刪除,視頻列表,收藏等等,如下圖所示)。????
PHCollectionListChangeRequest,繼承自NSObject。
PHContentEditingInput ,繼承自NSObject。
PHContentEditingOutput,繼承自NSObject。
PHFetchOptions,繼承自NSObject。獲取資源時的參數,可以傳 nil,即使用系統默認值。
PHFetchResult,繼承自NSObject。表示一系列的資源結果集合,也可以是相冊的集合,從????PHCollection 的類方法中獲得。
PHImageRequestOptions,繼承自NSObject??刂萍虞d圖片時的一系列參數。
PHLivePhoto,繼承自NSObject,實時照片的可顯示表示 - 包括拍攝前后瞬間的動作和聲音的圖片。
PHLivePhotoEditingContext,繼承自NSObject。
PHObject,繼承自NSObject,Photos模型對象(資產和集合)的抽象超類。
PHPhotoLibrary,繼承自NSObject,一個共享對象,用于管理對用戶照片庫的訪問和更改。
PHImageManager,繼承自NSObject,提供用于檢索或生成與Photos資源相關聯的預覽縮略圖和全尺寸圖像或視頻數據的方法。
PhotoKit 定義了與系統的 Photos 應用內展現給用戶的模型對象相一致的實體圖表。這些照片實體都是輕量級的不可變對象。所有的 PhotoKit 對象都是繼承自?PHObject?抽象基類,其公共接口只提供了一個?localIdentifier?屬性。
PHAsset?表示用戶照片庫中一個單獨的資源,用以提供資源的元數據。
成組的資源叫做資源集合,用?PHAssetCollection?類表示。一個單獨的資源集合可以是照片庫中的一個相冊或者一個時刻,或者是一個特殊的“智能相冊”。這種智能相冊包括所有的視頻集合,最近添加的項目,用戶收藏,所有連拍照片等等。PHAssetCollection?是?PHCollection?的子類。
PHCollectionList?表示一組的?PHCollections。因為它本身就是?PHCollection,所以集合列表可以包含其他集合列表,它們允許復雜的集合繼承。實際上,我們可以在照片應用的時刻欄目中看到它:照片 --- 時刻 --- 精選 --- 年度,就是一個例子。
獲取 (Fetch) 照片實體
獲取 vs. 枚舉
那些熟悉 AssetsLibrary 框架的開發者可能會記得 AssetsLibrary 可以用一些特定屬性來找到需要的資源,其中一個必須枚舉用戶資源庫來獲得匹配的資源。不得不承認,這個 API 雖然提供了一些縮小搜索域的方法,但還是十分低效。
而與之形成鮮明對比,PhotoKit 實體的實例是通過獲取得到的。那些熟悉 Core Data 的人,會覺得和 PhotoKit 在概念和描述都比較接近。
獲取請求
獲取操作是由上面描述的實體的類方法實現的。要使用哪個類/方法,取決于問題所在范圍和你展示與遍歷照片庫的方式。所有獲取方法的命名都是相似的:class func fetchXXX(..., options: PHFetchOptions) -> PHFetchResult?。options?參數給了我們一個對結果進行過濾和排序的途徑,這和?NSFetchRequest?的?predicate?與?sortDescriptors?參數類似。
獲取結果
你可能已經注意到了這些獲取操作不是異步的。它們返回了一個?PHFetchResult?對象,可以用類似?NSArray的接口來訪問結果內的集合。它會按需動態加載內容并且緩存最近請求的內容。這個行為和設置了?batchSize屬性的?NSFetchRequest?返回的結果數組相似。對于?PHFetchResult?來說,沒有辦法用參數來指定這個行為,但是官網文檔保證 “即使在處理大量的返回結果時,依然能夠有最好的表現”。
就算滿足請求的照片庫內容發生了改變,獲取方法所返回的?PHFetchResult?對象是不會自動更新。在后面的小節中,我們會介紹如何對返回的?PHFetchResult?對象的改變進行觀察并處理更新內容。
就算滿足請求的照片庫內容發生了改變,獲取方法所返回的?PHFetchResult?對象是不會自動更新。在后面的小節中,我們會介紹如何對返回的?PHFetchResult?對象的改變進行觀察并處理更新內容。
臨時集合 (Transient Collections)
你可能會發現你已經設計了一個可以操作資源集合的組件,并且你還希望它能夠處理任意一組的資源。PhotoKit 通過臨時資源集合,讓我們可以輕松做到這點。
你可以通過?PHAsset?對象數組或是包含資源的?PHFetchResult?對象來創建臨時資源集合。創建的操作在?PHAssetCollection?的?transientAssetCollectionWithAssets(...)?和?transientAssetCollectionWithFetchResult(...)?工廠方法內完成。這些方法創建出來的對象可以像其它的?PHAssetCollection?對象一樣使用。盡管如此,這些集合不會被存儲到用戶照片庫,自然也不會在照片應用中展示出來。
和資源集合相似,你可以用?PHCollectionList?中的?transientCollectionListWithXXX(...)?工廠方法來創建臨時集合列表。
當你要合并兩個獲取請求時,你就會發現這個東西非常有用。
照片元數據
正如文章開頭提到的,PhotoKit 提供了額外的關于用戶資源的元數據,而這些數據在以前使用 ALAssetsLibrary 框架中是沒有辦法訪問,或者很難訪問到。
HDR 和全景照片
你可以使用照片資源的?mediaSubtypes?屬性驗證資源庫中的圖像在捕捉時是否開啟了 HDR,拍攝時是否使用了相機應用的全景模式。
收藏和隱藏資源
要驗證一個資源是否被用戶標記為收藏或被隱藏,只要檢查?PHAsset?實例的?favorite?和?hidden?屬性即可。
連拍模式照片
對于一個資源,如果其?PHAsset?的?representsBurst?屬性為?true,則表示這個資源是一系列連拍照片中的代表照片 (多張照片是在用戶按住快門時拍攝的)。它還有一個屬性是?burstIdentifier,如果想要獲取連拍照片中的剩余的其他照片,可以通過將這個值傳入?fetchAssetsWithBurstIdentifier(...)?方法來獲取。
用戶可以在連拍的照片中做標記;此外,系統也會自動用各種試探來標記用戶可能會選擇的潛在代表照片。這個元數據是可以通過?PHAsset?的?burstSelectionTypes?屬性來訪問。這個屬性是用三個常量組成的位掩碼:.UserPick?表示用戶手動標記的資源,.AutoPick?表示用戶可能標記的潛在資源,.None?表示沒有標記的資源。
照片加載
在處理用戶照片庫的過去幾年中,開發者創造了上百 (如果沒有上千) 的小技巧來提高照片加載和展示的效率。這些技巧處理請求的派發和取消,圖像大小的修改和裁剪,緩存等等。PhotoKit 提供了一個可以用更加便捷和現代的 API 做了所有這些操作的類:PHImageManager?。
請求圖像
圖像請求是通過?requestImageForAsset(...)?方法派發的。這個方法接受一個?PHAsset,可以設置返回圖像的大小和圖像的其它可選項 (通過?PHImageRequestOptions?參數對象設置),以及結果回調 (result handler)。這個方法的返回值可以用來在所請求的數據不再被需要時取消這個請求。
圖像的尺寸和裁剪
奇怪的是,對返回圖像的尺寸定義和裁剪的參數是分布在兩個地方的。targetSize?和?contentMode?這倆參數會被直接傳入?requestImageForAsset(...)?方法內。這個 content Mode 和 UIView 的?contentMode?參數類似,決定了照片應該以按比例縮放還是按比例填充的方式放到目標大小內。注意:如果不對照片大小進行修改或裁剪,那么方法參數是?PHImageManagerMaximumSize?和?PHImageContentMode.Default?。
此外,PHImageRequestOptions?還提供了一些方式來確定圖像管理器該以怎樣的方式來重新設置圖像大小。resizeMode?屬性可以設置為?.Exact?(返回圖像必須和目標大小相匹配),.Fast?(比 .Exact 效率更高,但返回圖像可能和目標大小不一樣) 或者?.None。還有個值得一提的是,normalizedCroppingMode?屬性讓我們確定圖像管理器應該如何裁剪圖像。注意:如果設置了?normalizedcroppingMode?的值,那么?resizeMode?需要設置為?.Exact。
請求遞送和進度
默認情況下,如果圖像管理器決定要用最優策略,那么它會在將圖像的高質量版本遞送給你之前,先傳遞一個較低質量的版本。你可以通過?deliveryMode?屬性來控制這個行為;上面所描述的默認行為的值為?.Opportunistic。如果你只想要高質量的圖像,并且可以接受更長的加載時間,那么將屬性設置為?.HighQualityFormat。如果你想要更快的加載速度,且可以犧牲一點圖像質量,那么將屬性設置為?.FastFormat。
你可以使用?PHImageRequestOptions?的?synchronous?屬性,讓?requestImage...?系列的方法變成同步操作。注意:當?synchronous?設為?true?時,deliveryMode?屬性就會被忽略,并被當作?.HighQualityFormat?來處理。
在設置這些參數時,一定要考慮到你的一些用戶有可能開啟了 iCloud 照片庫,這點非常重要。PhotoKit 的 API 不一定會對設備的照片和 iCloud 上照片進行區分 —— 它們都用同一個?requestImage?方法來加載。這意味著任意一個圖像請求都有可能是一個通過蜂窩網絡來進行的非常緩慢的網絡請求。當你要用?.HighQualityFormat?或者做一個同步請求的時候,要牢記這個。注意:如果你想要確保請求不經過網絡,可以將?networkAccessAllowed?設為?false?。
另一個和 iCloud 相關的屬性是?progressHandler。你可以將它設為一個?PHAssetImageProgressHandler?的 block,當從 iCloud 下載照片時,它就會被圖像管理器自動調用。
資源版本
PhotoKit 允許應用對照片進行無損的修改。對編輯過的照片,系統會對單獨保存一份原始照片的拷貝和針對應用的調整數據。當用圖像管理器獲取資源時,你可以指定哪個版本的圖像資源應該通過 result handler 被遞送。這可以通過設置?version?屬性來做到:.Current?會遞送包含所有調整和修改的圖像;.Unadjusted?會遞送未被施加任何修改的圖像;.Original?會遞送原始的、最高質量的格式的圖像 (例如 RAW 格式的數據。而當將屬性設置為?.Unadjusted?時,會遞送一個 JPEG)。
你可以在 Sam Davies 的文章《照片擴展》中,閱讀框架中更多關于這方面的內容。
結果回調 (result handler)
結果回調是一個包含了一個?UIImage?變量和一個?info?字典作為參數的 block。根據參數和請求的選項,在請求的整個生命周期,它可以被圖像管理器多次調用。
info?字典提供了關于當前請求狀態的信息,比如:
圖像是否必須從 iCloud 請求 (如果你初始化時將?networkAccessAllowed?設置成?false,那么就必須重新請求圖像) ——?PHImageResultIsInCloudKey?。
當前遞送的?UIImage?是否是最終結果的低質量格式。當高質量圖像正在下載時,這個可以讓你給用戶先展示一個預覽圖像 ——?PHImageResultIsDegradedKey。
請求 ID (可以便捷的取消請求),以及請求是否已經被取消 ——?PHI
mageResultRequestIDKey?和?PHImageCancelledKey。
如果沒有圖像提供給 result handler,字典內還會有一個錯誤信息 ——?PHImageErrorKey。
這些值可以讓你更新你的 UI 來告知用戶,和上面討論到的?progressHandler?一起,來表示出它們的加載狀態。
緩存
當圖像即將要展示在屏幕上時,比如當要在一組滾動的 collection 視圖上展示大量的資源圖像的縮略圖時,預先將一些圖像加載到內存中有時是非常有用的。PhotoKit 提供了一個?PHImageManager?的子類來處理這種特定的使用場景 ——?PHImageCachingManager。
PHImageCachingManager?提供了一個關鍵方法 ——?startCachingImagesForAssets(...)。你傳入一個?PHAssets?類型的數組,一些請求參數,以及一些請求單個圖像時即將用到的可選項。此外,還有一些方法可以讓你通知緩存管理器來停止緩存特定資源列表,以及停止緩存所有圖像。
allowsCachingHighQualityImages?屬性可以讓你指定圖像管理器是否應該準備高質量圖像。當緩存一個較短和不變的資源列表時,默認?true?的屬性表現很好。但當要在 collection 視圖上快速滑動時做緩存操作的話,最好將它設置成?false?。
注意:以我的經驗,當用戶正在有大量資源的 collection 視圖上極其快速的滑動時,使用緩存管理器會損害滑動的表現效果。為這種特定的使用場景定制一個緩存行為是極其重要的。緩存窗口的大小,移動緩存窗口的時機和頻率,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 的 update handler 時所需要的信息。注意,若要正確的更新?UITableView/UICollectionView,你必須以正確順序來處理變化,那就是:RICE?——?removedIndexes,insertedIndexes,changedIndexes,enumerateMovesWithBlock (如果?hasMoves?為?true?的話)。另外,PHFetchResultChangeDetails?的?hasIncrementalChanges?屬性可以被設置成?false,這意味著舊的獲取結果應該全部被新的值代替。這種情況下,你應該調用?UITableView/UICollectionView?的?reloadData。
注意:沒有必要以集中的方式處理變化。如果你應用中的多個組件需要處理照片實體,那么它們每個都要有自己的?PHPhotoLibraryChangeObserver?。接著組件就能靠自己查詢?PHChange?對象,檢測是否需要 (以及如何) 更新它們自己的狀態。
隨風而變
現在我們已經知道了如何觀察用戶和其他應用造成的變化,我們來嘗試一下自己進行改變。
改變存在的對象
用 PhotoKit 在照片庫做改變,說到底其實是先創建了一個鏈接到某個資源或者資源集合的變化請求對象,再設置請求對象的相關屬性或調用合適的方法來描述你想要提交的變化。這個必須通過?performChanges(...)?方法,在提交到共享的?PHPhotoLibrary?的 block 內完成。注意:你需要準備好在?performChanges?方法的 completion block 里處理失敗的情況。雖然處理的是能被多個參與者 (如你的應用,用戶,其他應用,照片擴展等) 改變的狀態,但這個方式能提供安全性,也相對易用。
想要修改資源,需要創建一個?PHAssetChangeRequest?。然后你就可以修改創建創建日期,資源位置,以及是否將隱藏資源,是否將資源看做用戶收藏等。此外,你還可以從用戶的庫里刪除資源。
類似地,若要修改資源集合或集合列表,需要創建一個?PHAssetCollectionChangeRequest?或?PHCollectionListChangeRequest?對象。然后你就可以修改集合標題,添加或刪除集合成員,或者完全刪除集合。
在你的變化提交到用戶照片庫前,系統會向用戶展示一個明確的獲取權限的警告框。
創建新對象
創建一個新資源的做法和修改已存在的資源類似。只要用?creationRequestForAssetFromXXX(...)?工廠方法,來創建變化請求,并傳入資源圖像數據 (或一個 URL)。如果你需要對新建的資源做額外的修改,你可以用創建變化請求的?placeholderForCreatedAsset?屬性。它會返回一個可用的 placeholder 來代替“真實的”?PHAsset?引用。
結論
我已經討論了 PhotoKit 的基礎知識,但仍然還有非常多的東西等著我們去發掘。你可以通過查看示例的各處的代碼,觀看?WWDC session?視頻學習更多內容,發掘更深的知識,然后寫一些自己的代碼!PhotoKit 為 iOS 開發者開啟了通往新世界可能性,在未來的數月或者數年里,我們肯定會看到更多基于這個基礎構建的富有創造性的優秀產品。
參閱自:ObjC中國的照片框架。