Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rotate image in share extension

I have this extension and it works perfect in app target but crash in share extension when trying to rotate image captured on camera. How to rotate image in share extension? Or maybe it possible to load image from Photo Library already in right orientation.

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


Crash screenshots:

crash screenshot #1 crash screenshot #2

like image 315
ChikabuZ Avatar asked Jul 12 '19 14:07

ChikabuZ


1 Answers

First of all it is clear that you have a memory crash. According to 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.

And from error it is clear that you exceed 120 mb. But you might wonder what is took so much memory.

According to Optimizing Images Written by Jordan Morgan:

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

So if we calculate size or 4032 x 3024 photo it will be... 46mb for 4 bit color and 79mb for 8 bit color. Pretty big, but still less that a limit...

Thing is - you have two copies of your image. One is original and second one - rotated.

To solve this issue you need load only rotated image into memory, without original. This can be done with 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)
    }
}

In sample app:

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

it reduced memory peaks from 180+mb to just 80mb.

like image 148
Sergey Kuryanov Avatar answered Sep 17 '22 03:09

Sergey Kuryanov