You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftData无法将PhotosPickerItem作为属性,求相册照片引用存储方案

问题原因

PhotosPickerItem是UI层的临时对象,不遵循PersistentModel协议,无法被SwiftData持久化存储,因此直接将其作为模型属性会触发错误。正确的思路是存储相册照片的唯一标识(PHAsset.localIdentifier),通过标识后续可重新获取照片资源,避免存储图片数据造成冗余。


可行实现方案

1. 配置照片库权限

在Info.plist中添加权限描述,确保能访问用户相册:

<key>NSPhotoLibraryUsageDescription</key>
<string>需要访问您的相册以选择和查看照片</string>

2. 修改SwiftData模型

将原有的[PhotosPickerItem]替换为存储照片标识的[String]:

import SwiftData
import Photos

@Model
final public class Thing {
    var name: String = ""
    var comment: String = ""
    var photoIdentifiers: [String] = [] // 存储PHAsset的唯一标识

    init(name: String, comment: String) {
        self.name = name
        self.comment = comment
    }
}

3. 改造照片选择器视图

将选中的PhotosPickerItem转换为PHAsset.localIdentifier并更新模型:

import SwiftUI
import PhotosUI
import Photos

struct ImagePickerView: View {
    var thing: Thing
    @Environment(\.modelContext) private var modelContext
    
    @State private var selectedItems: [PhotosPickerItem] = []
    
    var body: some View {
        PhotosPicker(selection: $selectedItems, maxSelectionCount: 5, matching: .images) {
            Text("选择图片")
                .font(.headline)
                .padding()
                .background(Color.blue)
                .foregroundColor(.white)
                .cornerRadius(8)
        }
        .onChange(of: selectedItems) { _, newItems in
            Task {
                var identifiers: [String] = []
                for item in newItems {
                    // 优先直接获取PHAsset标识
                    if let assetID = item.assetIdentifier {
                        identifiers.append(assetID)
                    } else {
                        // 处理iCloud未下载照片的情况:先保存到本地相册再获取标识
                        if let data = try? await item.loadTransferable(type: Data.self),
                           let uiImage = UIImage(data: data),
                           let asset = saveImageToPhotos(uiImage) {
                            identifiers.append(asset.localIdentifier)
                        }
                    }
                }
                // 更新模型并持久化
                thing.photoIdentifiers = identifiers
                try? modelContext.save()
            }
        }
    }
    
    // 辅助方法:将图片保存到相册并返回对应的PHAsset
    private func saveImageToPhotos(_ image: UIImage) -> PHAsset? {
        var asset: PHAsset?
        let semaphore = DispatchSemaphore(value: 0)
        
        PHPhotoLibrary.shared().performChanges {
            let request = PHAssetCreationRequest.forAsset()
            request.addResource(with: .photo, data: image.jpegData(compressionQuality: 0.8)!, options: nil)
            asset = request.placeholderForCreatedAsset?.asset
        } completionHandler: { success, error in
            if !success {
                print("保存图片失败: \(error?.localizedDescription ?? "未知错误")")
            }
            semaphore.signal()
        }
        
        semaphore.wait()
        return asset
    }
}

4. 加载并显示照片

通过存储的localIdentifier获取PHAsset,再加载图片:

import SwiftUI
import Photos

struct PhotoDisplayView: View {
    var thing: Thing
    
    var body: some View {
        ScrollView(.horizontal) {
            HStack(spacing: 12) {
                for identifier in thing.photoIdentifiers {
                    if let asset = PHAsset.fetchAssets(withLocalIdentifiers: [identifier], options: nil).firstObject {
                        AsyncImage(url: asset.url) { phase in
                            switch phase {
                            case .empty:
                                ProgressView()
                            case .success(let image):
                                image.resizable()
                                    .scaledToFit()
                                    .frame(height: 100)
                                    .cornerRadius(8)
                            case .failure:
                                Image(systemName: "photo.badge.exclamationmark")
                                    .frame(height: 100)
                            @unknown default:
                                EmptyView()
                            }
                        }
                    }
                }
            }
            .padding()
        }
    }
}

// PHAsset扩展:获取图片的本地URL
extension PHAsset {
    var url: URL? {
        var result: URL?
        let semaphore = DispatchSemaphore(value: 0)
        
        let options = PHContentEditingInputRequestOptions()
        options.isNetworkAccessAllowed = true // 允许访问iCloud照片
        
        self.requestContentEditingInput(with: options) { input, _ in
            result = input?.fullSizeImageURL
            semaphore.signal()
        }
        
        semaphore.wait()
        return result
    }
}

核心说明

  • 存储PHAsset.localIdentifier而非图片数据,完全避免了数据冗余,同时保证能随时从相册获取最新的照片资源。
  • 针对iCloud未下载的照片,通过临时保存到本地相册的方式补充获取标识,确保兼容性。
  • 操作SwiftData时必须通过modelContext保存变更,保证数据持久化生效。

内容的提问来源于stack exchange,提问作者JohnSF

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 09:57:28