iOS 相機與相簿整合功能教學

讓使用者上傳頭像或分享照片是常見的功能。在 iOS 16+,我們使用 PhotosPicker 來存取相簿,這不需要使用者授權即可使用(因為是 Out-of-process 執行的)。

選擇照片 (PhotosPicker)

單張照片選擇

import PhotosUI

struct PhotoView: View {
    @State private var selectedItem: PhotosPickerItem?
    @State private var selectedImage: Image?
    
    var body: some View {
        VStack {
            if let selectedImage {
                selectedImage
                    .resizable()
                    .scaledToFit()
                    .frame(width: 300, height: 300)
            }
            
            PhotosPicker(selection: $selectedItem, matching: .images) {
                Label("選擇照片", systemImage: "photo")
            }
            .onChange(of: selectedItem) { newItem in
                Task {
                    // 將選中的 Item 轉換成 Data 再轉成 Image
                    if let data = try? await newItem?.loadTransferable(type: Data.self),
                       let uiImage = UIImage(data: data) {
                        selectedImage = Image(uiImage: uiImage)
                    }
                }
            }
        }
    }
}

拍照 (Camera)

SwiftUI 目前沒有內建的 Camera View。我們必須使用 UIViewControllerRepresentable 來包裝 UIKit 的 UIImagePickerController

struct CameraView: UIViewControllerRepresentable {
    @Binding var image: UIImage?
    @Environment(\.dismiss) var dismiss
    
    func makeUIViewController(context: Context) -> UIImagePickerController {
        let picker = UIImagePickerController()
        picker.sourceType = .camera // 設定來源為相機
        picker.delegate = context.coordinator
        return picker
    }
    
    func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
    
    func makeCoordinator() -> Coordinator { Coordinator(self) }
    
    class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
        let parent: CameraView
        init(_ parent: CameraView) { self.parent = parent }
        
        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
            if let image = info[.originalImage] as? UIImage {
                parent.image = image
            }
            parent.dismiss()
        }
    }
}

儲存照片到相簿

如果你有生成圖片或濾鏡功能,可能需要將結果存回相簿。這需要 Privacy - Photo Library Additions Usage Description 權限。

func saveToAlbum(image: UIImage) {
    UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
}

權限設定 (Info.plist)

使用相機或存取相簿時,必須在 Info.plist 中宣告對應的 Privacy Key,否則 App 會直接崩潰。

功能Key (Info.plist)說明
相機Privacy - Camera Usage Description拍攝照片或錄影
儲存照片Privacy - Photo Library Additions Usage Description僅寫入權限 (存照片到相簿)
讀取相簿Privacy - Photo Library Usage Description舊版 API 讀取照片 (PhotosPicker 不需要此權限)