WWDC Quick Look 💓 By SwiftGGTeam
Meet the new Photos picker

Meet the new Photos picker

Watch original video

Highlight

iOS 14 introduces a new Photos picker (PHPickerViewController), replacing the previousUIImagePickerControllerand manually request album permissions and then usePHAssetFiltering method.

Core Content

Many apps only want to complete a small task: change the avatar, attach a picture to a chat message, import a few photos into the editor. A common practice in the past was to request full photo gallery permissions and then usePhotoKitor oldUIImagePickerControllerBring in the photos. This scope of authority is too large. The user only clicks “Select Photos” once, but the App may gain access to the entire photo library.

iOS 14PHPickerViewControllerChange this process to a system agent. Selectors are provided by the system and run in a separate process outside of the app. The app cannot see other content in the selector, nor can it intercept the selector interface; only the photos and videos selected by the user will be sent back to the app as results. (01:06)

This change also solves the experience problem of the old selector. The new Photos picker has the same grid, search, zoom, multi-select, and selected content preview as the system Photos app. Developers use configuration objects to limit the optional types and number of choices, and then receive the results through delegates. (00:24)

The boundaries given by Apple are clear. Most apps that only require photo or video data do not need to be used directlyPhotoKit. Only necessary operations such as non-destructive image editing and photo library organizationPHAssetApps should request photo library permissions and handle iOS 14’s Limited Photos Library. (10:15)

Detailed Content

Configure selection range and number of multiple selections

02:27PHPickerConfigurationIt’s the entrance. It controls two key decisions: the maximum number of items the user can select, and which media types can appear in the selector.

import PhotosUI

var configuration = PHPickerConfiguration()

// “unlimited” selection by specifying 0, default is 1
configuration.selectionLimit = 0

// Only show images (including Live Photos)
configuration.filter = .images
// Uncomment next line for other example: Only show videos or Live Photos (for their video complement), but no images
// configuration.filter = .any(of: [.videos, .livePhotos])

Key points:

  • import PhotosUIIntroduce the frame where the Photos picker is located. -PHPickerConfiguration()Create a variable configuration object, and the subsequent selection number and filter conditions are written here. -selectionLimitDefault is 1; set to 0 for unlimited multiple selection. -.imagesPictures are displayed, including Live Photos. -.any(of: [.videos, .livePhotos])is another filter example from the official snippet, used to display the video portion of videos and Live Photos.

Render picker and receive results

(03:07) After the configuration is completed, use it to initializePHPickerViewController. The app itself is responsible for presenting and closing the picker; after the user confirms, the delegate will get[PHPickerResult]

import UIKit
import PhotosUI

class SingleSelectionPickerViewController: UIViewController, PHPickerViewControllerDelegate {
    @IBAction func presentPicker(_ sender: Any) {
        var configuration = PHPickerConfiguration()
        // Only wants images
        configuration.filter = .images

        let picker = PHPickerViewController(configuration: configuration)
        picker.delegate = self

        // The client is responsible for presentation and dismissal
        present(picker, animated: true)
    }

    func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
        // The client is responsible for presentation and dismissal
        picker.dismiss(animated: true)

        // Get the first item provider from the results, the configuration only allowed one image to be selected
        let itemProvider = results.first?.itemProvider

        if let itemProvider = itemProvider, itemProvider.canLoadObject(ofClass: UIImage.self) {
            itemProvider.loadObject(ofClass: UIImage.self) { (image, error) in
                // TODO: Do something with the image or handle the error
            }
        } else {
            // TODO: Handle empty results or item provider not being able load UIImage
        }
    }
}

Key points:

  • The controller needs to comply withPHPickerViewControllerDelegate, otherwise the user selection result will not be received. -PHPickerViewController(configuration:)Turn the previous configuration into the system selector interface. -picker.delegate = selfSpecifies that the current controller receives the selection completion callback. -didFinishPickingofresultsMay be empty; this occurs when the user deselects. -PHPickerResultpassitemProviderProvide data representation, use it before loadingcanLoadObject(ofClass:)Check type. -loadObject(ofClass:)It is an asynchronous API. Errors must be handled in the callback and UI updates must be switched back to the main thread.

Load images on demand when multiple selections are made

(07:34) Multiple selection does not require loading all images into memory at once. The demo app only saves the returnedNSItemProvider, and then use the iterator to load the next image on demand.

import UIKit
import PhotosUI

class ViewController: UIViewController {

    @IBOutlet weak var imageView: UIImageView!

    var itemProviders: [NSItemProvider] = []
    var iterator: IndexingIterator<[NSItemProvider]>?

    @IBAction func presentPicker(_ sender: Any) {
        var configuration = PHPickerConfiguration()
        configuration.filter = .images
        configuration.selectionLimit = 0

        let picker = PHPickerViewController(configuration: configuration)
        picker.delegate = self
        present(picker, animated: true)
    }

    func displayNextImage() {
        if let itemProvider = iterator?.next(), itemProvider.canLoadObject(ofClass: UIImage.self) {
            let previousImage = imageView.image
            itemProvider.loadObject(ofClass: UIImage.self) { [weak self] image, error in
                DispatchQueue.main.async {
                    guard let self = self, let image = image as? UIImage, self.imageView.image == previousImage else { return }
                    self.imageView.image = image
                }
            }
        }
    }

    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        displayNextImage()
    }

}

extension ViewController: PHPickerViewControllerDelegate {

    func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
        dismiss(animated: true)

        itemProviders = results.map(\.itemProvider)
        iterator = itemProviders.makeIterator()
        displayNextImage()
    }

}

Key points:

  • selectionLimit = 0When unlimited multi-selection is turned on, picker will display an interface and Add button suitable for multi-selection. -itemProvidersOnly save data provider references, do not decode all images in advance. -iteratorRecord the current display progress,displayNextImage()Only the next item is processed at a time. -previousImageandguardUsed to prevent asynchronous callbacks from overwriting changedimageView
  • touchesEndedUsed in the demo to switch to the next image, a real app can apply the same logic to buttons, pagination, or thumbnail lists. -results.map(\.itemProvider)Convert all selection results into subsequently loadable data sources.

Connect back to PhotoKit when PHAsset is needed

(11:13) A few apps need to continue to be usedPhotoKit, such as non-destructive editing or photo library organization. Can be used at this timePHPhotoLibraryInitialize the configuration and read it in the resultassetIdentifier, then use the standardPhotoKit API fetch PHAsset

import UIKit
import PhotosUI

class PhotoKitPickerViewController: UIViewController, PHPickerViewControllerDelegate {
    @IBAction func presentPicker(_ sender: Any) {
        let photoLibrary = PHPhotoLibrary.shared()
        let configuration = PHPickerConfiguration(photoLibrary: photoLibrary)
        let picker = PHPickerViewController(configuration: configuration)
        picker.delegate = self
        present(picker, animated: true)
    }

    func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
        picker.dismiss(animated: true)

        let identifiers = results.compactMap(\.assetIdentifier)
        let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: identifiers, options: nil)

        // TODO: Do something with the fetch result if you have Photos Library access
    }
}

Key points:

  • PHPhotoLibrary.shared()Get the current photo library object and configure the object using it andPhotoKitAlignment. -PHPickerConfiguration(photoLibrary:)It’s a needPHAssetThe initialization method used for identifiers. -results.compactMap(\.assetIdentifier)Filter out results without local asset identifiers. -PHAsset.fetchAssets(withLocalIdentifiers:options:)Convert identifier toPHAssetQuery results.
  • The comment emphasizes the permission prerequisite: only apps that already have Photos Library access can perform subsequent processing on the fetch results.
  • Limited Photos Library will not automatically expand as the user selects more photos in the pickerPHAssetAccessible scope; when additional authorization is required, a dedicated limited library management process must be used.

Core Takeaways

  1. What to do: Change avatar upload to one-time Photos picker

Why it’s worth doing: You only need one picture to upload an avatar, you shouldn’t ask for full photo library permission first.PHPickerViewControllerYou can allow the user to directly select a picture, and the App will only get the result of this selection.

How ​​to get started: CreatePHPickerConfiguration(),set upconfiguration.filter = .images, keep the defaultselectionLimit = 1,existdidFinishPickingpassitemProvider.loadObject(ofClass: UIImage.self)Read the picture.

  1. What to do: Add multiple image insertions to chat or note-taking apps

Why it’s worth doing: The session shows the picker’s multi-selection, selection preview and Add button, allowing users to select a group of pictures at one time.

How ​​to start: Putconfiguration.selectionLimitSet to 0 or the quantity allowed by the product; save in callbackresults.map(\.itemProvider), loaded one by one in message sending order or insertion order.

  1. What to do: Create a low-privilege import entry for the image editor

Why it’s worth it: Many editors only require the image data currently selected by the user, without browsing the entire photo library. returned by pickerNSItemProviderAlready able to loadUIImage

How ​​to start: Use firstPHPickerViewControllerDo a default import; if an advanced function really wants to trackPHAsset, and then request photo library access at the function entrance.

  1. What to do: Make a PhotoKit fork for a photo library organization app

Why it’s worth doing: Apps such as organizing photo albums and non-destructive editing are neededPHAssetand photo gallery access status. session gives the result taken from pickerassetIdentifierfetch againPHAssetpath.

How ​​to start: UsePHPickerConfiguration(photoLibrary:)Initialize selector; collect in callbackassetIdentifier;Detect if user has granted Photos Library access; needs to be expandedPHAssetWhen accessing the scope, access the additional authorization process mentioned in session.

Comments

GitHub Issues · utterances