coiai Logo

This article is an English translation of the original Japanese post. Read the Japanese original →

[SwiftUI] Implementing Camera Functionality — Dev Diary

December 23, 2023

Categories: 未分類

Notes on how to implement camera functionality in SwiftUI.

Implementing the basic functionality

import SwiftUI
import AVFoundation

struct CameraContentView: View {
    var body: some View {
        CameraView()
            .edgesIgnoringSafeArea(.all)
    }
}

struct CameraView: UIViewRepresentable {
    func makeUIView(context: Context) -> UIView { BaseCameraView() }
    func updateUIView(_ uiView: UIViewType, context: Context) {}
}

class BaseCameraView: UIView {
    override func layoutSubviews() {
        super.layoutSubviews()
        _ = initCaptureSession
        (layer.sublayers?.first as? AVCaptureVideoPreviewLayer)?.frame = bounds
    }

    lazy var initCaptureSession: Void = {
        guard let device = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera],
                                                            mediaType: .video,
                                                            position: .unspecified)
                .devices.first(where: { $0.position == .front }),
              let input = try? AVCaptureDeviceInput(device: device) else { return }

        let session = AVCaptureSession()
        session.addInput(input)
        session.startRunning()

        layer.insertSublayer(AVCaptureVideoPreviewLayer(session: session), at: 0)
    }()
}

Running this code launches the front camera and displays its feed across the full screen.

Rotating the camera to match the screen orientation

As is, when you rotate the device, the preview keeps its 16:9 portrait/landscape ratio while rotating along with it, so the camera feed no longer matches the screen's orientation.

So I added the following code.

enum Orientation: Int {
    case right = -1, down = 0, left = 1, up = 2
}

private func getDeviceOrientation() -> Orientation {
    guard let interfaceOrientation = UIApplication.shared.windows.first(where: { $0.isKeyWindow })?.windowScene?.interfaceOrientation else {
        return .right
    }
    switch interfaceOrientation {
    case .landscapeLeft:
        return .left
    case .landscapeRight:
        return .right
    case .portrait:
        return .down
    case .portraitUpsideDown:
        return .up
    default:
        return .right
    }
}

private func fixCameraOrientation() {
    let rotation: CGFloat = CGFloat(getDeviceOrientation().rawValue) * 90
    previewLayer?.transform = CATransform3DMakeRotation(rotation / 180 * CGFloat.pi, 0, 0, 1)
}

Adding these functions to layoutSubviews makes the camera orientation adjust correctly every time the device orientation changes.

References

This article was a huge help, thank you

https://zenn.dev/platina/articles/5b9683f6d50938

[SwiftUI] Implementing Camera Functionality — Dev Diary | coiai Inc.