PHPickerViewController
PHPickerViewController
, introduced in iOS 14, is a modern, privacy-focused replacement for UIImagePickerController
for selecting photos and videos from the Photo Library. It provides a system-provided UI for media selection with enhanced user control and privacy features, ensuring apps only access explicitly selected items. This document covers the usage of PHPickerViewController
in UIKit-based iOS applications.
Purpose
- Media Selection: Allows users to select photos, videos, or live photos from the Photo Library.
- Privacy: Limits app access to only user-selected assets, aligning with iOS privacy standards.
- Modern UI: Offers a customizable, native interface with filtering and search capabilities.
Key Features
- Limited Access: Apps only access media the user explicitly selects, without requiring full library permission.
- Flexible Configuration: Supports filtering by media type, limiting selection count, and preselecting assets.
- Delegate: Uses a delegate to handle selection results and dismissal.
- No Camera: Unlike
UIImagePickerController
, it only supports Photo Library access.
Basic Usage
PHPickerViewController
is presented modally, configured with a PHPickerConfiguration
, and requires a delegate conforming to PHPickerViewControllerDelegate
.
Example: Selecting a Single Image
import UIKit
import PhotosUI
class ViewController: UIViewController, PHPickerViewControllerDelegate {
@IBOutlet weak var imageView: UIImageView!
@IBAction func pickImage(_ sender: UIButton) {
var configuration = PHPickerConfiguration()
configuration.filter = .images // Only images
configuration.selectionLimit = 1 // Single selection
let picker = PHPickerViewController(configuration: configuration)
picker.delegate = self
present(picker, animated: true, completion: nil)
}
// Delegate method for selection
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
dismiss(animated: true, completion: nil)
guard let result = results.first else { return }
if result.itemProvider.canLoadObject(ofClass: UIImage.self) {
result.itemProvider.loadObject(ofClass: UIImage.self) { [weak self] image, error in
DispatchQueue.main.async {
if let image = image as? UIImage {
self?.imageView.image = image
} else if let error = error {
print("Error loading image: \(error)")
}
}
}
}
}
}
Key Points
- Configuration: Create a
PHPickerConfiguration
to specify filters, selection limits, and other options. - Filter: Use
.images
,.videos
, or.livePhotos
to restrict media types. - Selection Limit: Set
selectionLimit
to 1 for single selection or >1 for multiple (0 means no limit). - Delegate: Implement
picker(_:didFinishPicking:)
to handle selected results. - Presentation: Present modally using
present(_:animated:completion:)
.
Advanced Features
Filtering Media
Configure the picker to show specific media types:
var configuration = PHPickerConfiguration()
configuration.filter = PHPickerFilter.any(of: [.images, .videos]) // Images and videos
Multiple Selection
Allow multiple selections:
configuration.selectionLimit = 3 // Allow up to 3 items
Preselected Assets
Preselect assets using asset identifiers (requires PHPhotoLibrary
access):
import Photos
var configuration = PHPickerConfiguration(photoLibrary: .shared())
configuration.preselectedAssetIdentifiers = ["assetID1", "assetID2"]
Handling Different Media Types
Process photos, videos, or live photos:
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
dismiss(animated: true, completion: nil)
for result in results {
let provider = result.itemProvider
if provider.hasItemConformingToTypeIdentifier(UTType.image.identifier) {
provider.loadObject(ofClass: UIImage.self) { image, error in
DispatchQueue.main.async {
if let image = image as? UIImage {
print("Loaded image")
}
}
}
} else if provider.hasItemConformingToTypeIdentifier(UTType.movie.identifier) {
provider.loadFileRepresentation(forTypeIdentifier: UTType.movie.identifier) { url, error in
DispatchQueue.main.async {
if let url = url {
print("Loaded video: \(url)")
}
}
}
}
}
}
Customizing Appearance
Set the preferred asset representation mode:
configuration.preferredAssetRepresentationMode = .current // or .compatible for compatibility
iPad Popover
On iPad, configure a popover for presentation:
picker.modalPresentationStyle = .popover
if let popover = picker.popoverPresentationController {
popover.sourceView = sender
popover.sourceRect = sender.bounds
popover.permittedArrowDirections = .any
}
Permissions
PHPickerViewController
does not require full Photo Library access, as it only provides access to user-selected assets. However, if using PHPhotoLibrary
for preselected assets or other advanced features, add the NSPhotoLibraryUsageDescription
key to Info.plist
:
<key>NSPhotoLibraryUsageDescription</key>
<string>We need photo library access to preselect assets.</string>
Request permission if needed:
import Photos
PHPhotoLibrary.requestAuthorization { status in
if status == .authorized {
// Configure picker with PHPhotoLibrary
}
}
Best Practices
- Privacy: Leverage
PHPickerViewController
’s limited access to enhance user privacy. - Asynchronous Loading: Handle media loading asynchronously to avoid blocking the main thread.
- Error Handling: Check for errors when loading media with
NSItemProvider
. - Media Types: Explicitly filter media types to match app requirements.
- iPad Support: Configure popovers for iPad to ensure proper presentation.
- Accessibility: Ensure the picker UI is VoiceOver-compatible.
- Testing: Test on iPhone and iPad, with various media types and selection limits.
Limitations
- No Camera:
PHPickerViewController
only supports Photo Library; useUIImagePickerController
for camera access. - iOS 14+: Available only on iOS 14 and later.
- Editing: Does not support in-place editing like
UIImagePickerController
; handle editing separately. - File Access: Video or large assets require file-based loading, which may involve file coordination.
Summary
PHPickerViewController
is a modern, privacy-focused solution for selecting photos and videos in UIKit apps. With flexible configuration for filtering, selection limits, and media types, it provides a seamless user experience while respecting privacy. By following best practices, developers can integrate media selection efficiently and securely in iOS apps.