草庐IT

ios - 照片扩展名:无法保存未定向为“向上”的图像

coder 2023-09-16 原文

TL; DR:iOS照片编辑扩展无法保存对照片的更改,除非这些更改是使用设备沿横向向左拍摄的。

我正在尝试在iOS上开发照片编辑扩展程序。

我的代码基于Xcode模板Apple's sample code以及在线上提供的一些教程。

我注意到有些照片在应用更改后无法保存;我收到一个警报视图,内容为:

无法保存更改

保存时发生错误。请稍后再试。



在网络上搜索时,我在堆栈溢出中遇到了以下两个问题:

  • iOS photo extension finishContentEditingWithCompletionHandler: Unable to Save Changes(已应用修复,不适用于我的情况)
  • IOS) Photo Extension Unable To Save Changes Issue(对这个问题没有有用的答案)

  • 我尝试了几种编辑扩展程序,只是为了确保设备没有问题,然后发现问题出在以下方面:
  • 我的应用程序
  • 苹果自己的示例代码
  • AppStore上的某些第三方应用程序(例如Litely),但其他应用程序(例如BitCam-我希望我可以与该应用程序的开发人员联系,以寻求一些提示...)。

  • 我注意到,对于库中给定的照片资产,问题总是会发生,或者永远不会发生。也就是说,它似乎取决于所编辑照片的某些属性(因此,在这种情况下,整个“再试一次”业务毫无意义)。

    我决定在finishContentEditing(completionHandler:)方法内设置一个断点(调用该断点是为了将修改后的图像保存到框架指定的URL),并检查在编辑 session 开始时传递的PHContentEditingInput对象的各种属性。

    我很快意识到,使用iPhone以 Portrait Portrait Upside Down Landscape Right 方向拍摄的照片总是会出现此问题,并且只有那时。可以在风景左(右侧的“主页”按钮)中拍摄的照片没有问题。

    苹果的示例代码的作用是:
  • CIIMage实例的fullSizeImageURL属性创建一个PHContentEditingInput实例。
  • 通过在其上调用applyingOrientation()并传递输入的fullSizeImageOrientation属性的值,从点#1创建面向图像的副本
  • 将适当的CoreImage滤镜应用于从#2点开始的全尺寸,定向图像。
  • 创建一个CIContext
  • 使用上下文调用writeJPEGRepresentation(of:to:colorSpace:),传递在#3中获得的修改后的CIImage,renderedContentURL中的PHContentEditingOutput和原始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)
            }
        }
    }
    

    (稍微重构为在此处发布。上面代码的bluk驻留在一个单独的方法中,从调度队列块中调用)

    当我尝试编辑使用(例如)横向右方向的设备拍摄的照片时:



    ...选择Apple的示例代码Photo Editing Extension:



    ...应用“棕褐色”过滤器并点击“完成”:



    ...我得到了可怕的警报:



    ...并且关闭它后,图像预览会以某种方式旋转到相对于“横向左”的方向:



    (即,以横向右拍摄的照片旋转了180度,以纵向拍摄的照片旋转了90度,等等。)

    轻按“完成”或“取消”,然后轻按“放弃更改”即可完成 session ,并且图像将还原为正确的方向:



    显然,我,Litely的开发人员以及Apple的2016年示例代码都没有意识到某些陷阱(但BitCam的开发人员却没有意识到)。

    发生了什么事?

    解决方法?

    如果我使用纵向方向的iPhone拍照并尝试对其进行编辑,则在调试器上fullSizeImageOrientation.right,并且编辑失败,如上所述。

    但是,如果我使用默认工具将图像旋转180度一次:



    ...保存,再次编辑,然后将再旋转180度(或90 + 270度,但始终在中进行两次单独的编辑),将其恢复为原始方向,再返回,然后再尝试编辑使用扩展名,现在fullSizeImageOrientation的值为.up,保存成功到。我相信这是因为此工具实际上是旋转像素数据,而不是仅仅修改方向元数据(事实上,它可以以任意角度裁剪和旋转,而不仅仅是90度的倍数,我认为这样就可以了。)

    当然,这将需要不方便的用户交互,因此这实际上不是解决方法(不过,在编程上是等效的)。

    附录:

    我正在使用Xcode 10.0,并且已在运行iOS 12 GM的iPhone 8和运行iOS 11.4.1的iPhone 5s上确认了以上内容。

    最佳答案

    我当然已经看到保存失败,因为定向的东西是错误的,但是以下架构目前似乎对我有用:

    func startContentEditing(with contentEditingInput: PHContentEditingInput, placeholderImage: UIImage) {
        self.input = contentEditingInput
        if let im = self.input?.displaySizeImage {
            self.displayImage = CIImage(image:im, options: [.applyOrientationProperty:true])!
            // ... other stuff depending on what the adjustment data was ...
        }
        self.mtkview.setNeedsDisplay()
    }
    func finishContentEditing(completionHandler: @escaping ((PHContentEditingOutput?) -> Void)) {
        DispatchQueue.global(qos:.default).async {
            let inurl = self.input!.fullSizeImageURL!
            let output = PHContentEditingOutput(contentEditingInput:self.input!)
            let outurl = output.renderedContentURL
            var ci = CIImage(contentsOf: inurl, options: [.applyOrientationProperty:true])!
            let space = ci.colorSpace!
            // ... apply real filter to `ci` based on user edits ...
            try! CIContext().writeJPEGRepresentation(
                of: ci, to: outurl, colorSpace: space)
            let data = // whatever
            output.adjustmentData = PHAdjustmentData(
                formatIdentifier: self.myidentifier, formatVersion: "1.0", data: data)
            completionHandler(output)
        }
    }
    

    关于ios - 照片扩展名:无法保存未定向为“向上”的图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52384281/

    有关ios - 照片扩展名:无法保存未定向为“向上”的图像的更多相关文章

    1. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

      我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

    2. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

      我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

    3. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

      我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

    4. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

      我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

    5. Ruby Readline 在向上箭头上使控制台崩溃 - 2

      当我在Rails控制台中按向上或向左箭头时,出现此错误:irb(main):001:0>/Users/me/.rvm/gems/ruby-2.0.0-p247/gems/rb-readline-0.4.2/lib/rbreadline.rb:4269:in`blockin_rl_dispatch_subseq':invalidbytesequenceinUTF-8(ArgumentError)我使用rvm来管理我的ruby​​安装。我正在使用=>ruby-2.0.0-p247[x86_64]我使用bundle来管理我的gem,并且我有rb-readline(0.4.2)(人们推荐的最少

    6. ruby-on-rails - Ruby 检查日期时间是否为 iso8601 并保存 - 2

      我需要检查DateTime是否采用有效的ISO8601格式。喜欢:#iso8601?我检查了ruby​​是否有特定方法,但没有找到。目前我正在使用date.iso8601==date来检查这个。有什么好的方法吗?编辑解释我的环境,并改变问题的范围。因此,我的项目将使用jsapiFullCalendar,这就是我需要iso8601字符串格式的原因。我想知道更好或正确的方法是什么,以正确的格式将日期保存在数据库中,或者让ActiveRecord完成它们的工作并在我需要时间信息时对其进行操作。 最佳答案 我不太明白你的问题。我假设您想检查

    7. ruby - 如何验证 IO.copy_stream 是否成功 - 2

      这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

    8. Ruby 文件 IO 定界符? - 2

      我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的

    9. ruby-on-rails - 添加回形针新样式不影响旧上传的图像 - 2

      我有带有Logo图像的公司模型has_attached_file:logo我用他们的Logo创建了许多公司。现在,我需要添加新样式has_attached_file:logo,:styles=>{:small=>"30x15>",:medium=>"155x85>"}我是否应该重新上传所有旧数据以重新生成新样式?我不这么认为……或者有什么rake任务可以重新生成样式吗? 最佳答案 参见Thumbnail-Generation.如果rake任务不适合你,你应该能够在控制台中使用一个片段来调用重新处理!关于相关公司

    10. c - mkmf 在编译 C 扩展时忽略子文件夹中的文件 - 2

      我想这样组织C源代码:+/||___+ext||||___+native_extension||||___+lib||||||___(Sourcefilesarekeptinhere-maycontainsub-folders)||||___native_extension.c||___native_extension.h||___extconf.rb||___+lib||||___(Rubysourcecode)||___Rakefile我无法使此设置与mkmf一起正常工作。native_extension/lib中的文件(包含在native_extension.c中)将被完全忽略。

    随机推荐