在共享扩展中旋转图像

Rotate image in share extension

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

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!
        }
    }
}


崩溃截图:

您是否尝试过使用您在 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()
}

您可以尝试使用更优化的 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
}

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

Memory limits for running app extensions are significantly lower than the memory limits imposed on a foreground app. On both platforms, the system may aggressively terminate extensions because users want to return to their main goal in the host app.

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

根据优化图像 作者:乔丹·摩根:

iOS essentially derives its memory hit from an image’s dimensions - whereas the actual file size has much less to do with it.

因此,如果我们计算尺寸或 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。