iosswiftuiimagepickercontroller

Getting the actual camera-preview frame when using UIImagePickerController


When using UIImagePickerController() it's easy to add an overlay image.

I'd like to dynamically get the "actual camera preview image area", i.e. indicated in a yellow box here:

enter image description here

(For example you might need to add an image guide overlay, crosshairs or such.)

If you poke around in Apple's views you'll soon see that CAMPreviewView is the best bet to get that frame.

Hence, I just cheaply find it using code like this that searches for "CAMPreviewView" in view stack. pc is the UIImagePickerController

if let foundFrame = pc.view.first(ofType: "CAMPreviewView")?.frame {
    yourOverlay.frame = foundFrame
}
else {
    print("Had to use a reasonable fallback ...")
    yourOverlay.frame = .. 
}

the yourOverlay is the

pc.cameraOverlayView = yourOverlay

(The yellow border image is indeed yourOverlay for the photo example above.)

This is a bit cheap and an anything could happen to Apple's code in the future.

Is there a better more authorized and standard way to add an "image overlay" on the image itself of UIImagePickerController and/or a more standard way to find the frame of the actual live preview?

Some code for the "CAMPreviewView" hack ...

extension UIView {

    var subviewsRecursive: [UIView] {
        return subviews + subviews.flatMap { $0.subviewsRecursive }
    }
    
    ///Find by type by string.
    func first(ofType: String) -> UIView? {
        return subviewsRecursive.first(where: {String(describing: $0).contains(ofType)})
    }
}

Solution

  • For the record, there is really no way to achieve this as of 2024.

    The only real "hack" is to find the view of type

    CAMPreviewView
    

    You can easily do that with code like ...

    extension UIView {
        var subviewsRecursive: [UIView] {
            return subviews + subviews.flatMap { $0.subviewsRecursive }
        }
        ///Find by type by string.
        func first(ofType: String) -> UIView? {
            return subviewsRecursive.first(where: {
               String(describing: $0).contains(ofType)
            })
        }
    }