在共享扩展中旋转图像

Posted

技术标签:

【中文标题】在共享扩展中旋转图像【英文标题】:Rotate image in share extension 【发布时间】:2019-07-12 14:20:51 【问题描述】:

我有这个扩展程序,它在应用程序目标中运行良好,但在尝试旋转相机上捕获的图像时在共享扩展程序中崩溃。如何?或者也许可以从照片库中加载图像已经在正确的方向。

extension UIImage 

    func fixOrientation() -> UIImage 
        switch imageOrientation 
        case .up:
            return self
        default:
            UIGraphicsBeginImageContextWithOptions(size, false, scale)
            draw(in: CGRect(origin: .zero, size: size)) //Thread 1: EXC_RESOURCE RESOURCE_TYPE_MEMORY (limit=120 MB, unused=0x0)
            let result = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            return result!
        
    

崩溃截图:

【问题讨论】:

你在主线程上做这个? @Gal,是的,我愿意。 我认为你使用了太多内存。 你会显示图片吗?如果不是,您可以使用其他方法来固定方向,例如通过 CIImage。显示它可能会使用与绘图调用相同的内存。您可以在实例化图形上下文时尝试使用更小的尺寸,然后生成更小的图像 @riadhluke,是的,我正在显示图像。 draw 调用使用比显示更多的内存... 【参考方案1】:

首先很明显你有内存崩溃。根据App Extension Programming Guide:

运行应用扩展的内存限制明显低于前台应用的内存限制。在这两个平台上,系统都可能会因为用户想要返回到他们在宿主应用中的主要目标而主动终止扩展。

从错误中很明显你超过了 120 mb。但您可能想知道是什么占用了这么多内存。

根据Optimizing Images Written by Jordan Morgan:

ios 本质上是从图像的尺寸中获取其内存影响 - 而实际文件大小与它的关系要小得多。

因此,如果我们计算尺寸或 4032 x 3024 照片,它将是...... 4 位颜色为 46mb,8 位颜色为 79mb。相当大,但仍然没有限制......

问题是 - 您有 两个 图像副本。一个是原始的,第二个是旋转的。

要解决此问题,您只需将旋转后的图像加载到内存中,而不需要原始图像。这可以通过Image I/O Framework 来完成:

extension UIImage 
    static func imageWithFixedOrientation(at url: URL) -> UIImage? 
        guard let imageSource = CGImageSourceCreateWithURL(url as CFURL, nil) else  return nil 

        guard let imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as? Dictionary<CFString, Any> else  return nil 

        guard
            let width = imageProperties[kCGImagePropertyPixelWidth] as? CGFloat,
            let height = imageProperties[kCGImagePropertyPixelHeight] as? CGFloat
            else  return nil 

        let options: [NSString: Any] = [
            kCGImageSourceThumbnailMaxPixelSize: max(width, height),
            kCGImageSourceCreateThumbnailFromImageAlways: true,
            kCGImageSourceCreateThumbnailWithTransform: true
        ]

        guard let cgImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options as CFDictionary) else  return nil 

        return UIImage(cgImage: cgImage)
    

在示例应用中:

extension ViewController: UIImagePickerControllerDelegate & UINavigationControllerDelegate 
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) 
        picker.dismiss(animated: true)
        guard let url = info[.imageURL] as? URL else  return 

        let image = UIImage.imageWithFixedOrientation(at: url)
    

它将内存峰值从 180+mb 减少到仅 80mb。

【讨论】:

如果我将图像大小限制为 2500x2500,它适用于我,使用 3000x3000 或原始 4032 x 3024 它会崩溃。【参考方案2】:

您可以尝试使用更优化的UIGraphicsImageRendererFormat,而不是使用旧的UIGraphicsBeginImageContextWithOptions,您可以在此post 中找到更多信息。因此您的代码将如下所示:

func fixOrientation() -> UIImage 
    let format = UIGraphicsImageRendererFormat()
    format.scale = scale
    format.prefersExtendedRange = true
    let renderer = UIGraphicsImageRenderer(size: size, format: format)
    let image = renderer.image(actions:  context in

        var workSize = size;
        workSize.width = floor(workSize.width / scale)
        workSize.height = floor(workSize.height / scale)

        // if the orientation is already correct
        // if image.imageOrientation == .up  draw image 

        var transform = CGAffineTransform.identity

        //TO-DO - set transform depends on current image orientation
        //transform =

        let ctx = context.cgContext
        ctx.concatenate(transform)

        guard let cgImageCopy = cgImage else 
            return
        

        switch imageOrientation 
        case .left, .leftMirrored, .right, .rightMirrored:
            ctx.draw(cgImageCopy, in: CGRect(x: 0.0, y:0.0, width: workSize.height, height: workSize.width))
            break;

        default:
            ctx.draw(cgImageCopy, in: CGRect(origin: .zero, size: workSize))
            break;
        
    )

    return image

【讨论】:

它在这一行崩溃:let image = renderer.image(actions: context in 能否提供有关崩溃的任何具体信息,因为我无法重现它? 崩溃相同:Thread 1: EXC_RESOURCE RESOURCE_TYPE_MEMORY (limit=120 MB, unused=0x0)。您应该使用相机中的照片检查真实设备。 或在这里:ctx.draw(cgImageCopy, in: CGRect(origin: .zero, size: workSize))【参考方案3】:

您是否尝试使用您在 didFinishPicking 委托方法中提供的函数?

func photoLibrary() 
    if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) 
        let myPickerController = UIImagePickerController()
        myPickerController.delegate = self
        myPickerController.sourceType = .photoLibrary
        self.present(myPickerController, animated: true, completion: nil)
    


func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) 
    picker.dismiss(animated: true)

    guard let image = info[.originalImage] as? UIImage else 
        print("No image found")
        return
    

    // Flip the image here
    self.mainImageView.image = image.fixOrientation()

【讨论】:

是共享扩展我不用这个委托方式,也没用。

以上是关于在共享扩展中旋转图像的主要内容,如果未能解决你的问题,请参考以下文章

如何在上传前在 iOS 共享扩展中缩小图像

如何在通过共享扩展选择的 NSUserDefaults 中存储多个图像

使用PIL在python中旋转并将expand参数设置为true时指定图像填充颜色

无法通过共享扩展打开/读取图像

arcmap10.2图片转化成图层

图像旋转由16*16扩展存入20*20的分块