【发布时间】:2019-02-22 08:30:41
【问题描述】:
TL; DR:iOS 照片编辑扩展无法保存对照片所做的更改,除非它们是在横向左侧方向的设备上拍摄的。
我正在尝试在 iOS 上开发照片编辑扩展。
我的代码基于 Xcode 模板 Apple's sample code 和几个在线可用的教程。
我注意到有些照片在应用更改后无法保存;我得到一个警报视图,内容如下:
无法保存更改
保存时出错。请稍后再试。
好的
在网络上搜索让我在 Stack Overflow 上找到以下两个问题:
- iOS photo extension finishContentEditingWithCompletionHandler: Unable to Save Changes(已应用修复,在我的情况下不起作用)
- IOS) Photo Extension Unable To Save Changes Issue(问题没有有用的答案)
我尝试了几个编辑扩展程序只是为了确保我的设备没有问题,发现问题出现在:
- 我的应用,
- Apple 自己的示例代码,
- 一些 AppStore 上的第三方应用程序(例如,Litely),但不是其他应用程序(例如,BitCam - 我希望我可以联系向该应用的开发者寻求一些提示...)。
我注意到,对于库中的给定照片资产,问题要么总是出现,要么永远不会出现。也就是说,它似乎取决于正在编辑的照片的某些属性(因此在这种情况下,整个“稍后再试”业务毫无意义)。
我决定在finishContentEditing(completionHandler:)方法内部设置一个断点(调用将修改后的图像保存到框架指定的URL),并检查在编辑开始时传递的PHContentEditingInput对象的各种属性会议。
我很快意识到,使用 iPhone 在纵向、上下颠倒或横向拍摄的照片总是会出现此问题方向,只有这样。在横向左侧(右侧的主页按钮)拍摄的照片可以毫无问题地保存。
Apple 的示例代码所做的是:
- 从
PHContentEditingInput实例的fullSizeImageURL属性创建一个CIIMage实例。 - 通过在其上调用
applyingOrientation()并传递输入的fullSizeImageOrientation属性的值,从点 #1 创建图像的定向副本。 - 将适当的 CoreImage 过滤器应用于从点 #2 开始的全尺寸定向图像。
- 创建一个
CIContext。 - 使用上下文调用
writeJPEGRepresentation(of:to:colorSpace:),传递#3 中获得的修改后的CIImage、PHContentEditingOutput中的renderedContentURL和原始CIImage的颜色空间。
实际代码:
DispatchQueue.global(qos: .userInitiated).async {
// Load full-size image to process from input.
guard let url = input.fullSizeImageURL
else { fatalError("missing input image url") }
guard let inputImage = CIImage(contentsOf: url)
else { fatalError("can't load input image to apply edit") }
// Define output image with Core Image edits.
let orientedImage = inputImage//.applyingOrientation(input.fullSizeImageOrientation)
let outputImage: CIImage
switch selectedFilterName {
case .some(wwdcFilter):
outputImage = orientedImage.applyingWWDCDemoEffect()
case .some(let filterName):
outputImage = orientedImage.applyingFilter(filterName, parameters: [:])
default:
outputImage = orientedImage
}
// Usually you want to create a CIContext early and reuse it, but
// this extension uses one (explicitly) only on exit.
let context = CIContext()
// Render the filtered image to the expected output URL.
if #available(OSXApplicationExtension 10.12, iOSApplicationExtension 10.0, *) {
// Use Core Image convenience method to write JPEG where supported.
do {
try context.writeJPEGRepresentation(of: outputImage, to: output.renderedContentURL, colorSpace: inputImage.colorSpace!)
completionHandler(output)
} catch let error {
NSLog("can't write image: \(error)")
completionHandler(nil)
}
} else {
// Use CGImageDestination to write JPEG in older OS.
guard let cgImage = context.createCGImage(outputImage, from: outputImage.extent)
else { fatalError("can't create CGImage") }
guard let destination = CGImageDestinationCreateWithURL(output.renderedContentURL as CFURL, kUTTypeJPEG, 1, nil)
else { fatalError("can't create CGImageDestination") }
CGImageDestinationAddImage(destination, cgImage, nil)
let success = CGImageDestinationFinalize(destination)
if success {
completionHandler(output)
} else {
completionHandler(nil)
}
}
}
(稍微重构后发布在这里。上面的代码块位于 separate 方法中,从调度队列块中调用)
当我尝试编辑使用设备以(例如)横向右方向拍摄的照片时:
...选择苹果的示例代码照片编辑扩展:
...应用“棕褐色”过滤器并点击“完成”:
...我收到了可怕的警报:
...在关闭它之后,图像预览会以某种方式旋转到相对于横向左的方向:
(即以横向拍摄的照片旋转180度,以纵向拍摄的照片旋转90度等)
点击“完成”或“取消”然后“放弃更改”结束会话,图像恢复到正确的方向:
显然,我、Litely 的开发人员和 Apple 的 2016 示例代码都没有意识到一些陷阱(但 BitCam 的开发人员却没有意识到)。
发生了什么事?
解决方法?
如果我用 iPhone 以 纵向 方向拍摄照片并尝试对其进行编辑,则在调试器上 fullSizeImageOrientation 为 .right,并且如前所述编辑失败。
但如果我使用默认工具将图像旋转一次 180 度:
...保存,再次编辑并旋转另一个 180 度(或者,90 + 270 度,但总是在两个单独的编辑),返回到它的原始方向,然后尝试使用扩展进行编辑,现在fullSizeImageOrientation的值是.up,和保存成功。我相信这是因为这个工具实际上会旋转像素数据,而不是仅仅修改方向元数据(事实上它可以在任意角度进行裁剪和旋转 em>,我认为不仅仅是 90 度的倍数...)
当然,这将需要不方便的用户交互,因此它并不是真正的解决方法(不过,程序化的等价物是)。
附录:
我使用的是 Xcode 10.0,以上已在运行 iOS 12 GM 的 iPhone 8 和运行 iOS 11.4.1 的 iPhone 5s 上得到证实。
【问题讨论】:
-
我的照片扩展似乎没有这个问题,但直到(如你所说)我解决了方向问题......我会告诉你我认为你可能会做错了,但您没有显示任何代码。
-
@matt 谢谢。现在,我基本上是在未经修改的情况下运行 Apple 的示例代码(问题中的链接)。
-
嗯,这不是很有帮助。我提供了我所做工作的大纲,但您必须弄清楚它与您所做的工作有何不同(如果有的话)。
-
@matt 对不起,我现在将实际代码添加到我的问题中。它是虽然本质上是在“Apple 的示例代码所做的是:...”(5 点项目符号列表)之后所描述的。
-
好的,只是检查一下! :)
标签: ios swift photosframework photokit