axiom-photo-library-ref
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePhoto Library API Reference
照片库API参考文档
Quick Reference
快速参考
swift
// SWIFTUI PHOTO PICKER (iOS 16+)
import PhotosUI
@State private var item: PhotosPickerItem?
PhotosPicker(selection: $item, matching: .images) {
Text("Select Photo")
}
.onChange(of: item) { _, newItem in
Task {
if let data = try? await newItem?.loadTransferable(type: Data.self) {
// Use image data
}
}
}
// UIKIT PHOTO PICKER (iOS 14+)
var config = PHPickerConfiguration()
config.selectionLimit = 1
config.filter = .images
let picker = PHPickerViewController(configuration: config)
picker.delegate = self
// SAVE TO CAMERA ROLL
try await PHPhotoLibrary.shared().performChanges {
PHAssetCreationRequest.creationRequestForAsset(from: image)
}
// CHECK PERMISSION
let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)swift
// SWIFTUI PHOTO PICKER (iOS 16+)
import PhotosUI
@State private var item: PhotosPickerItem?
PhotosPicker(selection: $item, matching: .images) {
Text("Select Photo")
}
.onChange(of: item) { _, newItem in
Task {
if let data = try? await newItem?.loadTransferable(type: Data.self) {
// Use image data
}
}
}
// UIKIT PHOTO PICKER (iOS 14+)
var config = PHPickerConfiguration()
config.selectionLimit = 1
config.filter = .images
let picker = PHPickerViewController(configuration: config)
picker.delegate = self
// SAVE TO CAMERA ROLL
try await PHPhotoLibrary.shared().performChanges {
PHAssetCreationRequest.creationRequestForAsset(from: image)
}
// CHECK PERMISSION
let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)PHPickerViewController (iOS 14+)
PHPickerViewController (iOS 14+)
System photo picker for UIKit apps. No permission required.
适用于UIKit应用的系统照片选择器,无需权限。
Configuration
配置
swift
import PhotosUI
var config = PHPickerConfiguration()
// Selection limit (0 = unlimited)
config.selectionLimit = 5
// Filter by asset type
config.filter = .images
// Use photo library (enables asset identifiers)
config = PHPickerConfiguration(photoLibrary: .shared())
// Preferred asset representation
config.preferredAssetRepresentationMode = .automatic // default
// .current - original format
// .compatible - converted to compatible formatswift
import PhotosUI
var config = PHPickerConfiguration()
// 选择数量限制(0表示无限制)
config.selectionLimit = 5
// 按资源类型筛选
config.filter = .images
// 使用照片库(启用资源标识符)
config = PHPickerConfiguration(photoLibrary: .shared())
// 首选资源表示模式
config.preferredAssetRepresentationMode = .automatic // 默认值
// .current - 原始格式
// .compatible - 转换为兼容格式Filter Options
筛选选项
swift
// Basic filters
PHPickerFilter.images
PHPickerFilter.videos
PHPickerFilter.livePhotos
// Combined filters
PHPickerFilter.any(of: [.images, .videos])
// Exclusion filters (iOS 15+)
PHPickerFilter.all(of: [.images, .not(.screenshots)])
PHPickerFilter.not(.livePhotos)
// Playback style filters (iOS 17+)
PHPickerFilter.any(of: [.cinematicVideos, .slomoVideos])swift
// 基础筛选器
PHPickerFilter.images
PHPickerFilter.videos
PHPickerFilter.livePhotos
// 组合筛选器
PHPickerFilter.any(of: [.images, .videos])
// 排除筛选器(iOS 15+)
PHPickerFilter.all(of: [.images, .not(.screenshots)])
PHPickerFilter.not(.livePhotos)
// 播放风格筛选器(iOS 17+)
PHPickerFilter.any(of: [.cinematicVideos, .slomoVideos])Presenting
展示选择器
swift
let picker = PHPickerViewController(configuration: config)
picker.delegate = self
present(picker, animated: true)swift
let picker = PHPickerViewController(configuration: config)
picker.delegate = self
present(picker, animated: true)Delegate
代理方法
swift
extension ViewController: PHPickerViewControllerDelegate {
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true)
for result in results {
// Get asset identifier (if using PHPickerConfiguration(photoLibrary:))
let identifier = result.assetIdentifier
// Load as UIImage
result.itemProvider.loadObject(ofClass: UIImage.self) { object, error in
guard let image = object as? UIImage else { return }
DispatchQueue.main.async {
self.displayImage(image)
}
}
// Load as Data
result.itemProvider.loadDataRepresentation(forTypeIdentifier: UTType.image.identifier) { data, error in
guard let data else { return }
// Use data
}
// Load Live Photo
result.itemProvider.loadObject(ofClass: PHLivePhoto.self) { object, error in
guard let livePhoto = object as? PHLivePhoto else { return }
// Use live photo
}
}
}
}swift
extension ViewController: PHPickerViewControllerDelegate {
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true)
for result in results {
// 获取资源标识符(仅当使用PHPickerConfiguration(photoLibrary:)时可用)
let identifier = result.assetIdentifier
// 加载为UIImage
result.itemProvider.loadObject(ofClass: UIImage.self) { object, error in
guard let image = object as? UIImage else { return }
DispatchQueue.main.async {
self.displayImage(image)
}
}
// 加载为Data
result.itemProvider.loadDataRepresentation(forTypeIdentifier: UTType.image.identifier) { data, error in
guard let data else { return }
// Use data
}
// 加载Live Photo
result.itemProvider.loadObject(ofClass: PHLivePhoto.self) { object, error in
guard let livePhoto = object as? PHLivePhoto else { return }
// Use live photo
}
}
}
}PHPickerResult Properties
PHPickerResult 属性
| Property | Type | Description |
|---|---|---|
| NSItemProvider | Provides selected asset data |
| String? | PHAsset identifier (if using photoLibrary config) |
| 属性 | 类型 | 描述 |
|---|---|---|
| NSItemProvider | 提供选中资源的数据 |
| String? | PHAsset标识符(仅当使用photoLibrary配置时可用) |
PhotosPicker (SwiftUI, iOS 16+)
PhotosPicker (SwiftUI, iOS 16+)
SwiftUI view for photo selection. No permission required.
用于照片选择的SwiftUI视图,无需权限。
Basic Usage
基础用法
swift
import SwiftUI
import PhotosUI
// Single selection
@State private var selectedItem: PhotosPickerItem?
PhotosPicker(selection: $selectedItem, matching: .images) {
Label("Select Photo", systemImage: "photo")
}
// Multiple selection
@State private var selectedItems: [PhotosPickerItem] = []
PhotosPicker(
selection: $selectedItems,
maxSelectionCount: 5,
matching: .images
) {
Text("Select Photos")
}swift
import SwiftUI
import PhotosUI
// 单选
@State private var selectedItem: PhotosPickerItem?
PhotosPicker(selection: $selectedItem, matching: .images) {
Label("Select Photo", systemImage: "photo")
}
// 多选
@State private var selectedItems: [PhotosPickerItem] = []
PhotosPicker(
selection: $selectedItems,
maxSelectionCount: 5,
matching: .images
) {
Text("Select Photos")
}Filters
筛选器
swift
// Images only
matching: .images
// Videos only
matching: .videos
// Images and videos
matching: .any(of: [.images, .videos])
// Live Photos
matching: .livePhotos
// Exclude screenshots (iOS 15+)
matching: .all(of: [.images, .not(.screenshots)])swift
// 仅图片
matching: .images
// 仅视频
matching: .videos
// 图片和视频
matching: .any(of: [.images, .videos])
// Live Photos
matching: .livePhotos
// 排除截图(iOS 15+)
matching: .all(of: [.images, .not(.screenshots)])Selection Behavior
选择行为
swift
PhotosPicker(
selection: $items,
maxSelectionCount: 10,
selectionBehavior: .ordered, // .default, .ordered, .continuous
matching: .images
) { ... }| Behavior | Description |
|---|---|
| Standard multi-select |
| Selection order preserved |
| Live updates as user selects (iOS 17+) |
swift
PhotosPicker(
selection: $items,
maxSelectionCount: 10,
selectionBehavior: .ordered, // .default, .ordered, .continuous
matching: .images
) { ... }| 行为 | 描述 |
|---|---|
| 标准多选模式 |
| 保留选择顺序 |
| 用户选择时实时更新(iOS 17+) |
Embedded Picker (iOS 17+)
嵌入式选择器(iOS 17+)
swift
PhotosPicker(
selection: $items,
maxSelectionCount: 10,
selectionBehavior: .continuous,
matching: .images
) {
Text("Select")
}
.photosPickerStyle(.inline) // Embed in view hierarchy
.photosPickerDisabledCapabilities([.selectionActions])
.photosPickerAccessoryVisibility(.hidden, edges: .all)| Style | Description |
|---|---|
| Modal sheet (default) |
| Embedded in view |
| Single row |
| Disabled Capability | Effect |
|---|---|
| Hide search bar |
| Hide albums |
| Hide selection review |
| Hide Add/Cancel |
| Accessory Visibility | Description |
|---|---|
| Per edge |
swift
PhotosPicker(
selection: $items,
maxSelectionCount: 10,
selectionBehavior: .continuous,
matching: .images
) {
Text("Select")
}
.photosPickerStyle(.inline) // 嵌入视图层级
.photosPickerDisabledCapabilities([.selectionActions])
.photosPickerAccessoryVisibility(.hidden, edges: .all)| 样式 | 描述 |
|---|---|
| 模态弹窗(默认) |
| 嵌入视图中 |
| 单行显示 |
| 禁用功能 | 效果 |
|---|---|
| 隐藏搜索栏 |
| 隐藏相册列表 |
| 隐藏选择预览 |
| 隐藏添加/取消按钮 |
| 附件可见性 | 描述 |
|---|---|
| 按边缘设置 |
HDR Preservation (iOS 17+)
HDR 保留(iOS 17+)
swift
PhotosPicker(
selection: $items,
matching: .images,
preferredItemEncoding: .current // Don't transcode, preserve HDR
) { ... }| Encoding | Description |
|---|---|
| System decides format |
| Original format, preserves HDR |
| Force compatible format |
swift
PhotosPicker(
selection: $items,
matching: .images,
preferredItemEncoding: .current // 不转码,保留HDR
) { ... }| 编码方式 | 描述 |
|---|---|
| 系统自动决定格式 |
| 原始格式,保留HDR |
| 强制转换为兼容格式 |
Loading Images from PhotosPickerItem
从PhotosPickerItem加载图片
swift
// Load as Data (most reliable)
if let data = try? await item.loadTransferable(type: Data.self),
let image = UIImage(data: data) {
// Use image
}
// Custom Transferable for direct UIImage
struct ImageTransferable: Transferable {
let image: UIImage
static var transferRepresentation: some TransferRepresentation {
DataRepresentation(importedContentType: .image) { data in
guard let image = UIImage(data: data) else {
throw TransferError.importFailed
}
return ImageTransferable(image: image)
}
}
}
// Usage
if let result = try? await item.loadTransferable(type: ImageTransferable.self) {
let image = result.image
}swift
// 加载为Data(最可靠)
if let data = try? await item.loadTransferable(type: Data.self),
let image = UIImage(data: data) {
// Use image
}
// 自定义Transferable直接获取UIImage
struct ImageTransferable: Transferable {
let image: UIImage
static var transferRepresentation: some TransferRepresentation {
DataRepresentation(importedContentType: .image) { data in
guard let image = UIImage(data: data) else {
throw TransferError.importFailed
}
return ImageTransferable(image: image)
}
}
}
// 使用示例
if let result = try? await item.loadTransferable(type: ImageTransferable.self) {
let image = result.image
}PhotosPickerItem Properties
PhotosPickerItem 属性
| Property | Type | Description |
|---|---|---|
| String | Unique identifier |
| [UTType] | Available representations |
| 属性 | 类型 | 描述 |
|---|---|---|
| String | 唯一标识符 |
| [UTType] | 可用的资源表示类型 |
PhotosPickerItem Methods
PhotosPickerItem 方法
swift
// Load transferable
func loadTransferable<T: Transferable>(type: T.Type) async throws -> T?
// Load with progress
func loadTransferable<T: Transferable>(
type: T.Type,
completionHandler: @escaping (Result<T?, Error>) -> Void
) -> Progressswift
// 加载可传输对象
func loadTransferable<T: Transferable>(type: T.Type) async throws -> T?
// 带进度的加载
func loadTransferable<T: Transferable>(
type: T.Type,
completionHandler: @escaping (Result<T?, Error>) -> Void
) -> ProgressPHPhotoLibrary
PHPhotoLibrary
Access and modify the photo library.
访问和修改照片库。
Authorization Status
授权状态
swift
// Check current status
let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)
// Request authorization
let newStatus = await PHPhotoLibrary.requestAuthorization(for: .readWrite)swift
// 检查当前状态
let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)
// 请求授权
let newStatus = await PHPhotoLibrary.requestAuthorization(for: .readWrite)PHAuthorizationStatus
PHAuthorizationStatus
| Status | Description |
|---|---|
| User hasn't been asked |
| Parental controls limit access |
| User denied access |
| Full access granted |
| Access to user-selected photos only (iOS 14+) |
| 状态 | 描述 |
|---|---|
| 用户尚未被询问授权 |
| 家长控制限制了访问权限 |
| 用户拒绝了访问权限 |
| 已授予完全访问权限 |
| 仅能访问用户选择的照片(iOS 14+) |
Access Levels
访问级别
swift
// Read and write
PHPhotoLibrary.requestAuthorization(for: .readWrite)
// Add only (save photos, no reading)
PHPhotoLibrary.requestAuthorization(for: .addOnly)swift
// 读写权限
PHPhotoLibrary.requestAuthorization(for: .readWrite)
// 仅添加权限(保存照片,无法读取)
PHPhotoLibrary.requestAuthorization(for: .addOnly)Limited Library Picker
受限图库选择器
swift
// Present picker to expand limited selection
@MainActor
func presentLimitedLibraryPicker() {
guard let viewController = UIApplication.shared.keyWindow?.rootViewController else { return }
PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: viewController)
}
// With completion handler
PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: viewController) { identifiers in
// identifiers: asset IDs user added
}swift
// 展示选择器以扩展受限访问范围
@MainActor
func presentLimitedLibraryPicker() {
guard let viewController = UIApplication.shared.keyWindow?.rootViewController else { return }
PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: viewController)
}
// 带完成回调
PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: viewController) { identifiers in
// identifiers: 用户添加的资源ID
}Performing Changes
执行修改操作
swift
// Async changes
try await PHPhotoLibrary.shared().performChanges {
// Create, update, or delete assets
}
// With completion handler
PHPhotoLibrary.shared().performChanges({
// Changes
}) { success, error in
// Handle result
}swift
// 异步修改
try await PHPhotoLibrary.shared().performChanges {
// 创建、更新或删除资源
}
// 带完成回调
PHPhotoLibrary.shared().performChanges({
// 修改操作
}) { success, error in
// 处理结果
}Change Observer
修改观察者
swift
class PhotoObserver: NSObject, PHPhotoLibraryChangeObserver {
override init() {
super.init()
PHPhotoLibrary.shared().register(self)
}
deinit {
PHPhotoLibrary.shared().unregisterChangeObserver(self)
}
func photoLibraryDidChange(_ changeInstance: PHChange) {
// Handle changes
guard let changes = changeInstance.changeDetails(for: fetchResult) else { return }
DispatchQueue.main.async {
// Update UI with new fetch result
let newResult = changes.fetchResultAfterChanges
}
}
}swift
class PhotoObserver: NSObject, PHPhotoLibraryChangeObserver {
override init() {
super.init()
PHPhotoLibrary.shared().register(self)
}
deinit {
PHPhotoLibrary.shared().unregisterChangeObserver(self)
}
func photoLibraryDidChange(_ changeInstance: PHChange) {
// 处理修改
guard let changes = changeInstance.changeDetails(for: fetchResult) else { return }
DispatchQueue.main.async {
// 使用新的查询结果更新UI
let newResult = changes.fetchResultAfterChanges
}
}
}PHAsset
PHAsset
Represents an asset in the photo library.
表示照片库中的资源。
Fetching Assets
获取资源
swift
// All photos
let allPhotos = PHAsset.fetchAssets(with: .image, options: nil)
// With options
let options = PHFetchOptions()
options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
options.fetchLimit = 100
options.predicate = NSPredicate(format: "mediaType == %d", PHAssetMediaType.image.rawValue)
let recentPhotos = PHAsset.fetchAssets(with: options)
// By identifier
let assets = PHAsset.fetchAssets(withLocalIdentifiers: [identifier], options: nil)swift
// 所有照片
let allPhotos = PHAsset.fetchAssets(with: .image, options: nil)
// 带选项的查询
let options = PHFetchOptions()
options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
options.fetchLimit = 100
options.predicate = NSPredicate(format: "mediaType == %d", PHAssetMediaType.image.rawValue)
let recentPhotos = PHAsset.fetchAssets(with: options)
// 通过标识符查询
let assets = PHAsset.fetchAssets(withLocalIdentifiers: [identifier], options: nil)Asset Properties
资源属性
| Property | Type | Description |
|---|---|---|
| String | Unique ID |
| PHAssetMediaType | |
| PHAssetMediaSubtype | |
| Int | Width in pixels |
| Int | Height in pixels |
| Date? | When taken |
| Date? | Last modified |
| CLLocation? | GPS location |
| TimeInterval | Video duration |
| Bool | Marked as favorite |
| Bool | In hidden album |
| 属性 | 类型 | 描述 |
|---|---|---|
| String | 唯一ID |
| PHAssetMediaType | |
| PHAssetMediaSubtype | |
| Int | 像素宽度 |
| Int | 像素高度 |
| Date? | 拍摄时间 |
| Date? | 最后修改时间 |
| CLLocation? | GPS位置 |
| TimeInterval | 视频时长 |
| Bool | 标记为收藏 |
| Bool | 在隐藏相册中 |
PHAssetMediaType
PHAssetMediaType
| Type | Value |
|---|---|
| 0 |
| 1 |
| 2 |
| 3 |
| 类型 | 值 |
|---|---|
| 0 |
| 1 |
| 2 |
| 3 |
PHAssetMediaSubtype
PHAssetMediaSubtype
| Subtype | Description |
|---|---|
| Panoramic photo |
| HDR photo |
| Screenshot |
| Live Photo |
| Portrait mode |
| Streamed video |
| Slo-mo video |
| Timelapse |
| Cinematic mode |
| 子类型 | 描述 |
|---|---|
| 全景照片 |
| HDR照片 |
| 截图 |
| Live Photo |
| 人像模式 |
| 流式视频 |
| 慢动作视频 |
| 延时摄影 |
| 电影模式 |
PHAssetCreationRequest
PHAssetCreationRequest
Create new assets in the photo library.
在照片库中创建新资源。
Creating from UIImage
从UIImage创建
swift
try await PHPhotoLibrary.shared().performChanges {
PHAssetCreationRequest.creationRequestForAsset(from: image)
}swift
try await PHPhotoLibrary.shared().performChanges {
PHAssetCreationRequest.creationRequestForAsset(from: image)
}Creating from File URL
从文件URL创建
swift
try await PHPhotoLibrary.shared().performChanges {
PHAssetCreationRequest.creationRequestForAssetFromImage(atFileURL: imageURL)
}
// For video
try await PHPhotoLibrary.shared().performChanges {
PHAssetCreationRequest.creationRequestForAssetFromVideo(atFileURL: videoURL)
}swift
try await PHPhotoLibrary.shared().performChanges {
PHAssetCreationRequest.creationRequestForAssetFromImage(atFileURL: imageURL)
}
// 视频创建
try await PHPhotoLibrary.shared().performChanges {
PHAssetCreationRequest.creationRequestForAssetFromVideo(atFileURL: videoURL)
}Creating with Resources
带资源参数创建
swift
try await PHPhotoLibrary.shared().performChanges {
let request = PHAssetCreationRequest.forAsset()
// Add photo resource
let options = PHAssetResourceCreationOptions()
options.shouldMoveFile = true // Move instead of copy
request.addResource(with: .photo, fileURL: photoURL, options: options)
// Set creation date
request.creationDate = Date()
// Set location
request.location = CLLocation(latitude: 37.7749, longitude: -122.4194)
}swift
try await PHPhotoLibrary.shared().performChanges {
let request = PHAssetCreationRequest.forAsset()
// 添加照片资源
let options = PHAssetResourceCreationOptions()
options.shouldMoveFile = true // 移动文件而非复制
request.addResource(with: .photo, fileURL: photoURL, options: options)
// 设置创建日期
request.creationDate = Date()
// 设置位置
request.location = CLLocation(latitude: 37.7749, longitude: -122.4194)
}Deferred Photo Proxy (iOS 17+)
延迟照片代理(iOS 17+)
Save camera proxy photos for background processing:
swift
// From AVCaptureDeferredPhotoProxy callback
try await PHPhotoLibrary.shared().performChanges {
let request = PHAssetCreationRequest.forAsset()
// Use .photoProxy to trigger deferred processing
request.addResource(with: .photoProxy, data: proxyData, options: nil)
}| Resource Type | Description |
|---|---|
| Standard photo |
| Video file |
| Deferred processing proxy (iOS 17+) |
| Edit adjustments |
保存相机代理照片以进行后台处理:
swift
// 从AVCaptureDeferredPhotoProxy回调中调用
try await PHPhotoLibrary.shared().performChanges {
let request = PHAssetCreationRequest.forAsset()
// 使用.photoProxy触发延迟处理
request.addResource(with: .photoProxy, data: proxyData, options: nil)
}| 资源类型 | 描述 |
|---|---|
| 标准照片 |
| 视频文件 |
| 延迟处理代理(iOS 17+) |
| 编辑调整数据 |
Getting Created Asset
获取创建的资源
swift
try await PHPhotoLibrary.shared().performChanges {
let request = PHAssetCreationRequest.forAsset()
request.addResource(with: .photo, fileURL: url, options: nil)
// Get placeholder for later fetching
let placeholder = request.placeholderForCreatedAsset
// placeholder.localIdentifier available after changes complete
}swift
try await PHPhotoLibrary.shared().performChanges {
let request = PHAssetCreationRequest.forAsset()
request.addResource(with: .photo, fileURL: url, options: nil)
// 获取占位符用于后续查询
let placeholder = request.placeholderForCreatedAsset
// placeholder.localIdentifier在修改完成后可用
}PHFetchResult
PHFetchResult
Ordered list of assets from a fetch.
查询返回的有序资源列表。
Properties
属性
| Property | Type | Description |
|---|---|---|
| Int | Number of items |
| T? | First item |
| T? | Last item |
| 属性 | 类型 | 描述 |
|---|---|---|
| Int | 项目数量 |
| T? | 第一个项目 |
| T? | 最后一个项目 |
Methods
方法
swift
// Access by index
let asset = fetchResult.object(at: 0)
let asset = fetchResult[0]
// Get multiple
let assets = fetchResult.objects(at: IndexSet(0..<10))
// Iteration
fetchResult.enumerateObjects { asset, index, stop in
// Process asset
if shouldStop {
stop.pointee = true
}
}
// Check contains
let contains = fetchResult.contains(asset)
let index = fetchResult.index(of: asset)swift
// 通过索引访问
let asset = fetchResult.object(at: 0)
let asset = fetchResult[0]
// 获取多个资源
let assets = fetchResult.objects(at: IndexSet(0..<10))
// 遍历
fetchResult.enumerateObjects { asset, index, stop in
// 处理资源
if shouldStop {
stop.pointee = true
}
}
// 检查是否包含
let contains = fetchResult.contains(asset)
let index = fetchResult.index(of: asset)PHImageManager
PHImageManager
Request images from assets.
从资源中请求图片。
Request Image
请求图片
swift
let manager = PHImageManager.default()
let options = PHImageRequestOptions()
options.deliveryMode = .highQualityFormat
options.resizeMode = .exact
options.isNetworkAccessAllowed = true // For iCloud photos
let targetSize = CGSize(width: 300, height: 300)
manager.requestImage(
for: asset,
targetSize: targetSize,
contentMode: .aspectFill,
options: options
) { image, info in
guard let image else { return }
// Check if this is the final image
let isDegraded = (info?[PHImageResultIsDegradedKey] as? Bool) ?? false
if !isDegraded {
// Final high-quality image
}
}swift
let manager = PHImageManager.default()
let options = PHImageRequestOptions()
options.deliveryMode = .highQualityFormat
options.resizeMode = .exact
options.isNetworkAccessAllowed = true // 允许访问iCloud照片
let targetSize = CGSize(width: 300, height: 300)
manager.requestImage(
for: asset,
targetSize: targetSize,
contentMode: .aspectFill,
options: options
) { image, info in
guard let image else { return }
// 检查是否为最终图片
let isDegraded = (info?[PHImageResultIsDegradedKey] as? Bool) ?? false
if !isDegraded {
// 最终高质量图片
}
}PHImageRequestOptions
PHImageRequestOptions
| Property | Type | Description |
|---|---|---|
| PHImageRequestOptionsDeliveryMode | Quality preference |
| PHImageRequestOptionsResizeMode | Resize behavior |
| Bool | Allow iCloud download |
| Bool | Synchronous request |
| Block | Download progress |
| Bool | Extra callback during deferred processing (iOS 17+) |
| 属性 | 类型 | 描述 |
|---|---|---|
| PHImageRequestOptionsDeliveryMode | 质量偏好 |
| PHImageRequestOptionsResizeMode | 缩放行为 |
| Bool | 允许下载iCloud照片 |
| Bool | 同步请求 |
| Block | 下载进度回调 |
| Bool | 延迟处理期间额外回调(iOS 17+) |
Secondary Degraded Image (iOS 17+)
次级低质量图片(iOS 17+)
For photos undergoing deferred processing, get an intermediate quality image:
swift
let options = PHImageRequestOptions()
options.allowSecondaryDegradedImage = true
// Callback order:
// 1. Low quality (immediate, isDegraded = true)
// 2. Medium quality (new, isDegraded = true) -- while processing
// 3. Final quality (isDegraded = false)对于正在进行延迟处理的照片,获取中间质量的图片:
swift
let options = PHImageRequestOptions()
options.allowSecondaryDegradedImage = true
// 回调顺序:
// 1. 低质量图片(立即返回,isDegraded = true)
// 2. 中等质量图片(新特性,isDegraded = true)-- 处理中
// 3. 最终质量图片(isDegraded = false)Delivery Modes
交付模式
| Mode | Description |
|---|---|
| Fast thumbnail, then high quality |
| Only high quality |
| Only fast/degraded |
| 模式 | 描述 |
|---|---|
| 先返回缩略图,再返回高质量图片 |
| 仅返回高质量图片 |
| 仅返回快速/低质量图片 |
Request Video
请求视频
swift
manager.requestAVAsset(forVideo: asset, options: nil) { avAsset, audioMix, info in
guard let avAsset else { return }
// Use AVAsset for playback
}
// Or export to file
manager.requestExportSession(
forVideo: asset,
options: nil,
exportPreset: AVAssetExportPresetHighestQuality
) { session, info in
session?.outputURL = outputURL
session?.outputFileType = .mp4
session?.exportAsynchronously { ... }
}swift
manager.requestAVAsset(forVideo: asset, options: nil) { avAsset, audioMix, info in
guard let avAsset else { return }
// 使用AVAsset进行播放
}
// 或导出到文件
manager.requestExportSession(
forVideo: asset,
options: nil,
exportPreset: AVAssetExportPresetHighestQuality
) { session, info in
session?.outputURL = outputURL
session?.outputFileType = .mp4
session?.exportAsynchronously { ... }
}PHChange
PHChange
Represents changes to the photo library.
表示照片库的修改。
Getting Change Details
获取修改详情
swift
func photoLibraryDidChange(_ changeInstance: PHChange) {
guard let changes = changeInstance.changeDetails(for: fetchResult) else { return }
// Check what changed
let hasIncrementalChanges = changes.hasIncrementalChanges
let insertedIndexes = changes.insertedIndexes
let removedIndexes = changes.removedIndexes
let changedIndexes = changes.changedIndexes
// Get new fetch result
let newResult = changes.fetchResultAfterChanges
// Update collection view
DispatchQueue.main.async {
if hasIncrementalChanges {
collectionView.performBatchUpdates {
if let removed = removedIndexes {
collectionView.deleteItems(at: removed.map { IndexPath(item: $0, section: 0) })
}
if let inserted = insertedIndexes {
collectionView.insertItems(at: inserted.map { IndexPath(item: $0, section: 0) })
}
if let changed = changedIndexes {
collectionView.reloadItems(at: changed.map { IndexPath(item: $0, section: 0) })
}
}
} else {
collectionView.reloadData()
}
}
}swift
func photoLibraryDidChange(_ changeInstance: PHChange) {
guard let changes = changeInstance.changeDetails(for: fetchResult) else { return }
// 检查修改内容
let hasIncrementalChanges = changes.hasIncrementalChanges
let insertedIndexes = changes.insertedIndexes
let removedIndexes = changes.removedIndexes
let changedIndexes = changes.changedIndexes
// 获取新的查询结果
let newResult = changes.fetchResultAfterChanges
// 更新集合视图
DispatchQueue.main.async {
if hasIncrementalChanges {
collectionView.performBatchUpdates {
if let removed = removedIndexes {
collectionView.deleteItems(at: removed.map { IndexPath(item: $0, section: 0) })
}
if let inserted = insertedIndexes {
collectionView.insertItems(at: inserted.map { IndexPath(item: $0, section: 0) })
}
if let changed = changedIndexes {
collectionView.reloadItems(at: changed.map { IndexPath(item: $0, section: 0) })
}
}
} else {
collectionView.reloadData()
}
}
}Common Code Patterns
常见代码模式
Complete Photo Gallery View
完整照片图库视图
swift
import SwiftUI
import Photos
@MainActor
class PhotoGalleryViewModel: ObservableObject {
@Published var assets: [PHAsset] = []
@Published var authorizationStatus: PHAuthorizationStatus = .notDetermined
func requestAccess() async {
authorizationStatus = await PHPhotoLibrary.requestAuthorization(for: .readWrite)
if authorizationStatus == .authorized || authorizationStatus == .limited {
fetchAssets()
}
}
func fetchAssets() {
let options = PHFetchOptions()
options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
options.fetchLimit = 100
let result = PHAsset.fetchAssets(with: .image, options: options)
assets = result.objects(at: IndexSet(0..<result.count))
}
func expandLimitedAccess(from viewController: UIViewController) {
PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: viewController)
}
}
struct PhotoGalleryView: View {
@StateObject private var viewModel = PhotoGalleryViewModel()
var body: some View {
Group {
switch viewModel.authorizationStatus {
case .authorized, .limited:
PhotoGridView(assets: viewModel.assets)
case .denied, .restricted:
PermissionDeniedView()
case .notDetermined:
RequestAccessView {
Task { await viewModel.requestAccess() }
}
@unknown default:
EmptyView()
}
}
.task {
await viewModel.requestAccess()
}
}
}swift
import SwiftUI
import Photos
@MainActor
class PhotoGalleryViewModel: ObservableObject {
@Published var assets: [PHAsset] = []
@Published var authorizationStatus: PHAuthorizationStatus = .notDetermined
func requestAccess() async {
authorizationStatus = await PHPhotoLibrary.requestAuthorization(for: .readWrite)
if authorizationStatus == .authorized || authorizationStatus == .limited {
fetchAssets()
}
}
func fetchAssets() {
let options = PHFetchOptions()
options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
options.fetchLimit = 100
let result = PHAsset.fetchAssets(with: .image, options: options)
assets = result.objects(at: IndexSet(0..<result.count))
}
func expandLimitedAccess(from viewController: UIViewController) {
PHPhotoLibrary.shared().presentLimitedLibraryPicker(from: viewController)
}
}
struct PhotoGalleryView: View {
@StateObject private var viewModel = PhotoGalleryViewModel()
var body: some View {
Group {
switch viewModel.authorizationStatus {
case .authorized, .limited:
PhotoGridView(assets: viewModel.assets)
case .denied, .restricted:
PermissionDeniedView()
case .notDetermined:
RequestAccessView {
Task { await viewModel.requestAccess() }
}
@unknown default:
EmptyView()
}
}
.task {
await viewModel.requestAccess()
}
}
}Resources
资源
Docs: /photosui/phpickerviewcontroller, /photosui/photospicker, /photos/phphotolibrary, /photos/phasset, /photos/phimagemanager
Skills: axiom-photo-library, axiom-camera-capture
文档: /photosui/phpickerviewcontroller, /photosui/photospicker, /photos/phphotolibrary, /photos/phasset, /photos/phimagemanager
技能: axiom-photo-library, axiom-camera-capture