iOS相册批量编辑:PHAssetChangeRequest报错与单权限弹窗实现
解决方案
问题根源
- 原代码中
requestContentEditingInput是异步方法,它的回调会在performChanges的事务闭包执行完毕后才触发,此时已不在照片库的事务上下文内,创建PHAssetChangeRequest会触发权限报错。 - 若直接在
performChanges闭包内同步创建修改请求,会因每张照片单独触发权限校验,导致多次弹窗。
正确实现思路
要实现批量修改仅弹一次权限弹窗,必须:
- 先异步批量获取所有选中照片的编辑输入并完成图片处理,生成对应的
PHContentEditingOutput - 待所有处理完成后,在同一个
performChanges事务内批量提交所有修改请求,系统会自动合并权限校验,只弹出一次确认框
代码实现
let options = PHContentEditingInputRequestOptions() options.canHandleAdjustmentData = { adjustmentData -> Bool in adjustmentData.formatIdentifier == AdjustmentFormatIdentifier && adjustmentData.formatVersion == "1.0" } // 用字典保存每个资产对应的编辑输出 var assetOutputMap: [PHAsset: PHContentEditingOutput] = [:] let dispatchGroup = DispatchGroup() for asset in imageAssets { guard let asset = asset else { continue } dispatchGroup.enter() asset.requestContentEditingInput(with: options) { contentEditingInput, info in defer { dispatchGroup.leave() } guard let contentEditingInput = contentEditingInput else { // 处理获取编辑输入失败的情况 return } // 1. 加载并处理图片 let url = contentEditingInput.fullSizeImageURL! let orientation = contentEditingInput.fullSizeImageOrientation guard var inputImage = CIImage(contentsOf: url) else { return } inputImage = inputImage.oriented(forExifOrientation: orientation) // 2. 应用变换滤镜 let transformFilter = CIFilter.lanczosScaleTransform() transformFilter.inputImage = inputImage transformFilter.scale = 1 transformFilter.aspectRatio = Float(stretchFactor) guard let outputImage = transformFilter.outputImage else { return } // 3. 创建调整数据 let filterData = withUnsafeBytes(of: stretchFactor) { Data($0) } let adjustmentData = PHAdjustmentData(formatIdentifier: AdjustmentFormatIdentifier, formatVersion: "1.0", data: filterData) // 4. 生成编辑输出并写入图片数据 let contentEditingOutput = PHContentEditingOutput(contentEditingInput: contentEditingInput) let jpegData = UIImage(ciImage: outputImage, scale: displayScale, orientation: .up).jpegData(compressionQuality: 1) do { try jpegData?.write(to: contentEditingOutput.renderedContentURL, options: .atomic) contentEditingOutput.adjustmentData = adjustmentData assetOutputMap[asset] = contentEditingOutput } catch { // 处理写入失败的情况 } } } // 所有图片处理完成后,统一提交修改 dispatchGroup.notify(queue: .main) { PHPhotoLibrary.shared().performChanges({ for (asset, output) in assetOutputMap { let request = PHAssetChangeRequest(for: asset) request.contentEditingOutput = output } }, completionHandler: { success, error in if success { isPresented = false } else { // 处理修改失败的情况 } }) }
关键说明
- 使用
DispatchGroup等待所有异步图片处理任务完成,确保所有PHContentEditingOutput准备就绪后再提交事务。 - 所有
PHAssetChangeRequest在同一个performChanges闭包内创建,系统会识别为批量操作,仅弹出一次权限确认弹窗(显示修改照片总数)。 - 异步处理图片编辑的逻辑被移出
performChanges事务,避免了事务超时或上下文失效的问题。
内容的提问来源于stack exchange,提问作者blabus
相关产品推荐
相关产品推荐

