1. Introduction

PhotoKit is the framework for apps to use and manage photos and videos, and includes iCloud images as well as live photos.

2. Profile

  • iniOS,PhotoKitSupport for application building photos and editing extensions, as well as direct access to managing photo and video meta-resources and meta-resource collections such as albums, moments and shared albums.

3. The official Demo – PhotoBrowse

Browsing and Modifying Photo Albums

This example demonstrates how to implement a similar layout using customization. It uses PhotoKit to take thumbnails of resources and then display them as a single photo, video, or moving image. The sample application, PhotoBrowse, also demonstrates how to organize a user’s photos into albums and built-in collections, such as recent additions and favorites. It supports the creation, deletion, and modification of albums, as well as the editing and collection of personal resources.

3.1 Get all albums, all photo requests

let allPhotosOptions = PHFetchOptions() allPhotosOptions.sortDescriptors = [NSSortDescriptor(key: "CreationDate ", Ascending: true)] allPhotos = phasSet. fetchAssets(with: AllPhotosOptions) / / get smart albums smartAlbums = PHAssetCollection fetchAssetCollections (with: smartAlbum, subtype: . AlbumRegular options: nil) / / get all the photo album of the user to create userCollections = PHCollectionList. FetchTopLevelUserCollections (with: nil)Copy the code

The fetch operation is implemented by the class methods of the entities described above. Which class/method to use depends on the scope of the problem and how you present and traverse the photo library. All fetchXXX methods are named similarly :class func fetchXXX(… , options: PHFetchOptions) -> phfetchresult. options gives us a way to filter and sort the results, similar to the predicate and sortDescriptors of NSFetchRequest.

3.2 Observing changes

First, you need to use a registerChangeObserver(…) through the shared PHPhotoLibrary object. Method to register a change observer to follow PHPhotoLibraryChangeObserver protocol (the observer). Change observer’s photoLibraryDidChange(…) as long as changes made by another app or user in the photo library affect any resources or collections of resources that you acquired before the change. Methods are called. This method takes a single parameter of type PHChange, which you can use to verify that the changes are related to the fetch object you are interested in.

PHChange provides several methods that allow you to track changes to any PHObject or PHFetchResult object you are interested in. These methods are changeDetailsForObject(…) And changeDetailsForFetchResult (…). . If there is no any change, the method returns nil, or you can use PHObjectChangeDetails or PHFetchResultChangeDetails objects to observe these changes.

PHObjectChangeDetails provides a reference to the latest photo entity object and Boolean values that tell you whether the object’s image data has changed or whether the object has been deleted. PHFetchResultChangeDetails encapsulates the imposed by getting before you get PHFetchResult change of information. PHFetchResultChangeDetails as much as possible is to simplify the CollectionView designed or TableView update operations. Its properties map exactly to the information you need to use a typical CollectionView update handler. Note that if you want to correct the UITableView update/UICollectionView, you have to deal with changes in the right order, that is: RICE – RemovedIndexes insertedIndexes, changedIndexes enumerateMovesWithBlock (if hasMoves to true). In addition, PHFetchResultChangeDetails hasIncrementalChanges properties can be set to false, which means that the old get all the results should be replaced by the new value. In that case, you should call 1 uitableview reloadData/UICollectionView1.

Note: It is not necessary to deal with change in a centralized manner. If you need to handle multiple components of the application of photo entity, so they each have their own PHPhotoLibraryChangeObserver. Components can then query PHChange objects on their own to see if (and how) they need to update their own state.

-photoliBraryDidChange phphotolibrary.shared ().register(self) func photoLibraryDidChange(_).register(self) func photoLibraryDidChange(_ changeInstance: PHChange) {// The notification may be received in the background thread, so UI updates here need to be placed in the main thread with dispatchqueue.main.sync {// Check each of the three top-level fetches for  changes. if let changeDetails = changeInstance.changeDetails(for: AllPhotos) {/ / update the cache data source allPhotos = changeDetails. FetchResultAfterChanges} / / update the cache data and updates the UI if let changeDetails = changeInstance.changeDetails(for: smartAlbums) { smartAlbums = changeDetails.fetchResultAfterChanges tableView.reloadSections(IndexSet(integer: Section.smartAlbums.rawValue), with: .automatic) } if let changeDetails = changeInstance.changeDetails(for: userCollections) { userCollections = changeDetails.fetchResultAfterChanges tableView.reloadSections(IndexSet(integer: Section.userCollections.rawValue), with: Automatic)}}} / / cancel listening PHPhotoLibrary. Shared () unregisterChangeObserver (self)Copy the code

3.3 Cache and display list thumbnails

Pre-loading some images into memory can sometimes be useful when images are about to be displayed on the screen, such as when a large number of resource image thumbnails are displayed in a scrolling collection view. PhotoKit provides a subclass of PHImageManager to handle this particular usage scenario – PHImageCachingManager.

StartCachingImagesForAssets PHImageCachingManager provides a key method (…). You pass in an array of type PHAssets, some request parameters, and some options to use when requesting a single image. In addition, there are ways to tell the cache manager to stop caching specific resource lists, as well as to stop caching all images.

By default, if the image manager decides to use an optimal policy, it will deliver a lower-quality version of the image before sending a higher-quality version to you. You can control this behavior with the deliveryMode property; The default behavior described above has a value of.opportunistic. If you only want high-quality images and can accept longer loading times, set the property to.highqualityFormat. If you want faster loading and can sacrifice a bit of image quality, set the property to. FastFormat.

You can use the synchronous button of PHImageRequestOptions to make the requestImage… Serial methods become synchronous operations. Note: When synchronous is set to true, the deliveryMode attribute is ignored and treated as if it were synchronous. HighQualityFormat. When setting these parameters, it’s important to keep in mind that some of your users may have access to the iCloud photo library. PhotoKit’s API doesn’t necessarily distinguish between photos on your device and photos on iCloud — they’re all loaded using the same requestImage method. This means that any image request can be a very slow network request over a cellular network. Keep this in mind when you’re using.high quality format or making a synchronization request. Note: If you want to ensure that requests do not go over the network, you can set networkAccessAllowed to false. Another icloud-related property is progressHandler. You can setting it to a PHAssetImageProgressHandler block, when from up to download photos, it will be the image manager automatically calls.

In this case, the data is cached before the page is displayed and then retrieved for display.

let (addedRects, removedRects) = differencesBetweenRects(previousPreheatRect, preheatRect) let addedAssets = addedRects .flatMap { rect in collectionView! .indexPathsForElements(in: rect) } .map { indexPath in fetchResult.object(at: indexPath.item) } let removedAssets = removedRects .flatMap { rect in collectionView! .indexPathsForElements(in: rect) } .map { indexPath in fetchResult.object(at: IndexPath. Item)} / / update the cache data imageManager startCachingImages (for: addedAssets targetSize: thumbnailSize, contentMode: .aspectFill, options: nil) imageManager.stopCachingImages(for: removedAssets, targetSize: thumbnailSize, contentMode: .aspectFill, options: nil)Copy the code
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "GridViewCell", for: IndexPath) as? GridViewCell else {fatalError("Unexpected cell in collection view") asset.mediaSubtypes.contains(.photoLive) { cell.livePhotoBadgeImage = PHLivePhotoView.livePhotoBadgeImage(options: OverContent)} / / request photo cell through PHCachingImageManager representedAssetIdentifier = asset. LocalIdentifier imageManager.requestImage(for: asset, targetSize: thumbnailSize, contentMode: .aspectFill, options: nil, resultHandler: { image, _ in // UIKit may have recycled this cell by the handler's activation time cell.representedAssetIdentifier == asset.localIdentifier { cell.thumbnailImage = image } })Copy the code

3.4 Editing and Modifying resource files

Using PhotoKit to make changes to a photo library is essentially creating a change request object linked to a resource or collection of resources, and then setting the properties of the request object or calling the appropriate methods to describe the changes you want to submit. This must be done through performChanges(…) Method within a block committed to the shared PHPhotoLibrary. Note: You need to be prepared to handle the failure in the Completion block of the performChanges method. While dealing with states that can be changed by multiple participants (your app, users, other apps, photo extensions, etc.), this approach provides security and is relatively easy to use.

To modify a resource, you need to create a PHAssetChangeRequest, and then you can change the creation date, location of the resource, whether to hide the resource, whether to treat the resource as a user favorite, and so on. In addition, you can delete resources from the user’s library. Similarly, if you want to modify a resource collection or collection list, you need to create a PHAssetCollectionChangeRequest or PHCollectionListChangeRequest objects. You can then modify the collection title, add or remove collection members, or delete the collection altogether.

Before your changes are submitted to the user photo library, the system will show the user a clear access warning box.

3.4.1 Modifying resources

DispatchQueue.global(qos: .userinitiated).async {// If you make a filter for a photo, you should make an adjustmentData object, which records what filters are selected, what parameters are configured, and how to use these filters. AdjustmentData = PHAdjustmentData(formatIdentifier: self.formatIdentifier, formatVersion: self.formatVersion, data: filterName.data(using: .utf8)!) Let output = PHContentEditingOutput(contentEditingInput: AdjustmentData = adjustmentData (adjustmentData) (String, PHContentEditingInput, PHContentEditingOutput, @escaping () -> Void) -> Void if self.asset.mediaSubtypes.contains(.photoLive) { applyFunc = self.applyLivePhotoFilter }  else if self.asset.mediaType == .image { applyFunc = self.applyPhotoFilter } else { applyFunc = self.applyVideoFilter } ApplyFunc (filterName, input, output, {// When the filter is submitted for use, it is updated to the repository. PHPhotoLibrary.shared().performChanges({ let request = PHAssetChangeRequest(for: self.asset) request.contentEditingOutput = output }, completionHandler: { success, error in if ! success { print("Can't edit the asset: \(String(describing: error))") } }) }) }Copy the code

3.4.2 Modifying a Resource Set or collection list

PHPhotoLibrary.shared().performChanges({ PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: title) }, completionHandler: { success, error in if ! success { print("Error creating album: \(String(describing: error)).") } })Copy the code

3.4.3 Adding a Resource

PHPhotoLibrary.shared().performChanges({ let creationRequest = PHAssetChangeRequest.creationRequestForAsset(from: image) if let assetCollection = self.assetCollection { let addAssetRequest = PHAssetCollectionChangeRequest(for: assetCollection) addAssetRequest? .addAssets([creationRequest.placeholderForCreatedAsset!]  as NSArray) } }, completionHandler: {success, error in if ! success { print("Error creating the asset: \(String(describing: error))") } })Copy the code

3.4.4 Deleting a Resource

if assetCollection ! = nil {/ / removed from the currently selected album | collection resources PHPhotoLibrary. Shared () performChanges ({let request = PHAssetCollectionChangeRequest (for: self.assetCollection)! Request.removeassets ([self.asset] as NSArray)}, completionHandler: completion)} else {// Delete assets directly from the repository. PHPhotoLibrary.shared().performChanges({ PHAssetChangeRequest.deleteAssets([self.asset] as NSArray) }, completionHandler: completion) }Copy the code

3.5 other

Check if it’s Favorite album, and add Favorite

PHPhotoLibrary.shared().performChanges({ let request = PHAssetChangeRequest(for: self.asset) request.isFavorite = ! self.asset.isFavorite }, completionHandler: { success, error in if success { DispatchQueue.main.sync { sender.title = self.asset.isFavorite ? "♡ ︎" : "♡"}} else {print("Can't mark the asset as a Favorite: \(String(error))")}})Copy the code

4. The API is introduced

4.1 PhotoKitOverview diagram of common classes used by the framework

4.2 PhotoKitFrame drawing

4.3 the commonly used class

Original: iOS photo frame

This basic

PHPhotoLibrary: represents the user’s photo library. It is used to request and obtain the permission of the photo library and monitor the changes of the photo library. `

4.3.2 resources

PHObject: Abstract base class that all other PhotoKit classes inherit from, providing a localIdentifier attribute;

PHAsset: Represents a single resource in the photo library (can be a picture or video; Similar to ALAsset) for retrieving and saving metadata for a resource; PHAsset contains only metadata (such as image size and creation date). Specific image and video data need to be loaded using PHImageManager.

If a resource representsBurst attribute to true, indicates that resource is series of representative photos of the shoot photos by fetchAssetsWithBurstIdentifier () method, the incoming burstIdentifier properties, Get the remaining photos in the continuous photos;

PHCollection: parent of PHAssetCollection and PHCollectionList;

PHAssetCollection: A collection of resources, representing groups of resources; Can represent a photo album, moment, smart album in the photo library;

Smart album: special albums provided by the system by default, such as recently deleted, video list, favorites, etc.

PHCollectionList: a collection representing a set of PHCollections; PHCollectionList can also contain other PhCollectionLists.

4.3.3 to obtain

PHFetchResult: represents the result set of a PHAsset, or the result set of a PHCollection;

PHFetchOptions: parameters passed in to get PHFetchResult. These parameters can be filtered by type, date, and name.

Pass nil and use the system default;

PHImageManager: used to load resources;

PHCachingImageManager: inherits from PHImageManager and loads cached resources. When using a large number of resources, you can first prepare resource images in the background to reduce the delay in the subsequent request for a single resource. For example, when you want to populate a collection view with thumbnails of photos and videos, use PHCachingImageManager. After first use startCachingImagesForAssets method for resource, also request of PHImageManager method is used to load resources;

PHImageRequestOptions: input parameter for the resource loading, which controls the output size of the resource.

4.3.4 update

PHAssetChangeRequest: Used to create, delete, and modify PHAsset objects;

PHAssetCollectionChangeRequest: used to create, delete, and modify PHAssetCollection object;

PHCollectionListChangeRequest: used to create, delete, and modify PHCollectionList object;

PHAssetCreationRequest: A subclass of PHAssetChangeRequest that can also be created to enrich the way resources are added;

4.3.5 Enumeration Values

Note: When the album of a specified type is obtained, the main type and subtype must match. If they do not match, the system will treat them as any subtype. For the moment type, the subtype uses any; For the smartAlbum type, the subtype uses albumRegular instead of any, which has one more Recently Deleted album.

enum PHAssetCollectionType : Case smartAlbum // Photos app built-in albums (content dynamic update) case moment // Photos App automatically generated time and place grouped album}Copy the code
enum PHAssetCollectionSubtype : Int {/ / PHAssetCollectionTypeAlbum regular subtypes case albumRegular set in Photos app / / the user's own album case albumSyncedEvent / / Has been abandoned; Case albumSyncedFaces // Case albumSyncedAlbum // Case albumSyncedAlbum // case albumSyncedAlbum // case albumSyncedAlbum // case albumSyncedAlbum // case albumSyncedAlbum // case albumSyncedAlbum // Case albumSyncedAlbum // Case albumSyncedAlbum // Case albumSyncedAlbum // Case albumSyncedAlbum // From the camera or external storage import album / / PHAssetCollectionTypeAlbum Shared subtypes case albumMyPhotoStream / / users up pictures of the case AlbumCloudShared / / / / users up to share the photo album / / PHAssetCollectionTypeSmartAlbum subtypes case smartAlbumGeneric / / not a special type of photo albums, Case smartAlbumPanoramas // Case smartAlbumVideos // Case smartAlbumVideos // Case smartAlbumVideos // Case SmartalBumanoramas // Case smartAlbumVideos // Case smartAlbumVideos SmartAlbumFavorites // Album of Favorite photos and videos Case smartAlbumTimelapses // Album of delayed videos case smartAlbumAllHidden // Album of hidden photos and videos case Case smartAlbumRecentlyAdded // Album of recent photos/videos taken by camera; Case smartAlbumSlomoVideos // of surge module Slomo stands for Slow Motion, high-speed photography, slow motion parsing (120 frames for iOS devices) case smartAlbumUserLibrary // Camera album, which contains all photos and videos taken by the camera, @available(iOS 9.0, *) Case smartAlbumSelfPortraits @available(iOS 9.0, *) Case smartAlbumScreenshots @available(iOS 10.2, *) Case smartAlbumDepthEffect @available(iOS 10.3, *) case smartAlbumLivePhotos @available(iOS 11.0, *) case smartAlbumAnimated @available(iOS 11.0, *) *) Case Smartalbumlong entitled // Used for fetching, if you don't care about the exact subtype case anyCopy the code

5. To summarize

Prior to iOS 8, developers could only use the AssetsLibrary framework to manipulate photos, but as Apple continues to update its phones, the device continues to evolve, and the photos feature continues to be updated, the original AssetsLibrary framework is no longer able to keep up. But with the arrival of iOS 8, Apple has provided us with a modern framework, PhotoKit, so that developers can better adapt to the trend, better development of relevant applications, so that applications and devices can work perfectly.

reference

Photos Frame Retrieves local image and video information

The photo frame

PHAdjustmentData