Swift 6中调用PHPhotoLibrary.performChanges保存图片崩溃问题
解决Swift 6中使用PHPhotoLibrary保存图片时出现的
IOSurfaceClientSetSurfaceNotify failed e00002c7错误 问题描述
原本在Swift 5中正常运行的相册保存代码,切换到Swift 6后点击保存按钮时触发错误提示:
IOSurfaceClientSetSurfaceNotify failed e00002c7
原代码如下:
import SwiftUI import Photos struct ContentView: View { var body: some View { VStack { Color.blue .frame(width: 100, height: 100) Button("Save") { if let imageData = ImageRenderer(content: self.body).uiImage?.pngData() { saveImageToPhotoLibrary(imageData: imageData) } } } } private func saveImageToPhotoLibrary(imageData: Data) { Task { let status = await PHPhotoLibrary.requestAuthorization(for: .addOnly) guard status == .authorized else { return } try await PHPhotoLibrary.shared().performChanges { let request = PHAssetCreationRequest.forAsset() request.addResource(with: .photo, data: imageData, options: nil) } } } }
问题原因
这个错误源于Swift 6对并发和UI线程访问的严格控制——代码中直接将self.body传入ImageRenderer,而body是View的计算属性,在Swift 6的并发环境下,跨线程访问UI相关资源会触发底层IOSurface的异常。
解决方案
方案1:单独提取目标渲染视图
将需要保存的视图部分单独抽离,避免直接引用整个View的body:
import SwiftUI import Photos struct ContentView: View { // 单独定义要保存的目标视图 private let saveTargetView: some View = Color.blue .frame(width: 100, height: 100) var body: some View { VStack { saveTargetView Button("Save") { // 渲染单独的目标视图而非整个body if let imageData = ImageRenderer(content: saveTargetView).uiImage?.pngData() { saveImageToPhotoLibrary(imageData: imageData) } } } } private func saveImageToPhotoLibrary(imageData: Data) { Task { let status = await PHPhotoLibrary.requestAuthorization(for: .addOnly) guard status == .authorized else { return } try await PHPhotoLibrary.shared().performChanges { let request = PHAssetCreationRequest.forAsset() request.addResource(with: .photo, data: imageData, options: nil) } } } }
方案2:强制渲染操作在主线程执行
通过自定义ImageRenderer的渲染闭包,确保UI绘制操作在主线程完成:
import SwiftUI import Photos struct ContentView: View { var body: some View { VStack { Color.blue .frame(width: 100, height: 100) Button("Save") { let renderer = ImageRenderer(content: self.body) // 指定渲染闭包,强制在主线程执行绘制 renderer.renderer = .init { size, context in DispatchQueue.main.sync { self.body.draw(in: CGRect(origin: .zero, size: size), with: context) } } if let imageData = renderer.uiImage?.pngData() { saveImageToPhotoLibrary(imageData: imageData) } } } } private func saveImageToPhotoLibrary(imageData: Data) { Task { let status = await PHPhotoLibrary.requestAuthorization(for: .addOnly) guard status == .authorized else { return } try await PHPhotoLibrary.shared().performChanges { let request = PHAssetCreationRequest.forAsset() request.addResource(with: .photo, data: imageData, options: nil) } } } }
补充说明
Swift 6强化了UI线程的访问限制,任何涉及UI绘制、资源访问的操作都必须在主线程执行。直接在非主线程访问View的body会导致底层IOSurface资源的冲突,通过上述两种方式确保UI渲染操作的线程安全性,即可解决该错误。
内容的提问来源于stack exchange,提问作者YRUsoDiao
相关产品推荐
相关产品推荐

