iphoneimageswiftuiid-card

Take a picture (ID Card in this case) through a frame with rounded edges using SwiftUI


I have managed to take a correct picture of an ID Card, however, to help the user to frame it I need to do it through a frame with rounded edges exactly as shown in the image. I have tried many approaches without success. Could someone give me some guidance?

import UIKit
import SwiftUI
import AVFoundation

struct CameraView: UIViewControllerRepresentable {
    @Binding var isShowing: Bool
    @Binding var capturedImage: UIImage?
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<CameraView>) -> UIViewController {
        let cameraView = UIImagePickerController()
        cameraView.sourceType = .camera
        cameraView.delegate = context.coordinator
        return cameraView
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<CameraView>) {

    }

    func makeCoordinator() -> Coordinator {
        return Coordinator(isShowing: $isShowing, capturedImage: $capturedImage)
    }

    class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
        @Binding var isShowing: Bool
        @Binding var capturedImage: UIImage?

        init(isShowing: Binding<Bool>, capturedImage: Binding<UIImage?>) {
            _isShowing = isShowing
            _capturedImage = capturedImage
        }

        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
            let image = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
            capturedImage = image
            isShowing = false
        }

        func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
            isShowing = false
        }
    }
}

This is my basic approach


Solution

  • The way to handle this is to use the UIImagePickerController.cameraOverlayView property. You assign a UIView to this which is presented over the top of the camera.

    Here's a (bad looking) example, using SwiftUI View and a UIHostingController

    struct Overlay: View {
    
        var body: some View {
            VStack(spacing: 0) {
                Rectangle().fill(.black.opacity(0.5))
                    .frame(height: 100)
                HStack(spacing: 0) {
                    Rectangle().fill(.black.opacity(0.5))
                        .frame(width: 20)
                    Color.clear
                    Rectangle().fill(.black.opacity(0.5))
                        .frame(width: 20)
                }
                .frame(maxWidth: .infinity)
                .frame(height: 200)
                Rectangle().fill(.black.opacity(0.5))
                Color.clear
                    .frame(height: 200)
            }
        }
    }
    

    and then

    func makeUIViewController(context: UIViewControllerRepresentableContext<CameraView>) -> UIViewController {
        let cameraView = UIImagePickerController()
        cameraView.sourceType = .camera
        cameraView.delegate = context.coordinator
        
        let overlayView = UIHostingController(rootView: Overlay()).view
        overlayView?.backgroundColor = .clear
        overlayView?.isUserInteractionEnabled = false
        overlayView?.frame = (cameraView.cameraOverlayView?.frame)!
        cameraView.cameraOverlayView = overlayView
        return cameraView
    }
    
    

    gives…

    enter image description here

    There are more details in the Apple documentation