This post covers implementing a camera in SwiftUI, specifically switching between the front and back camera.
I'm sure other sites cover various approaches to this, but I wanted to keep it here as a note to myself.
Setting up the toggle button
This is a button with a camera icon. Tapping it should switch the camera.
Button(action: {
camera.switchCamera()
}) {
Image(systemName: "arrow.triangle.2.circlepath.camera")
.font(.largeTitle)
.padding()
}
.background(Color.white.opacity(0.7))
.clipShape(Circle())
Functionality
Just add the following code to the previous post's project and you're good to go.
func switchCamera() {
currentCameraPosition = currentCameraPosition == .front ? .back : .front
updateCameraPosition()
}
Full code
No reason to hold anything back, so here's the entire code so far.
If you copy and paste this and it doesn't work, this post has probably gone stale by then.
import SwiftUI
import AVFoundation
struct CameraView: View {
@StateObject var camera = CameraViewModel()
var body: some View {
ZStack {
CameraControll(camera: camera)
.edgesIgnoringSafeArea(.all)
VStack {
Spacer()
Button(action: {
camera.switchCamera()
}) {
Image(systemName: "arrow.triangle.2.circlepath.camera")
.font(.largeTitle)
.padding()
}
.background(Color.white.opacity(0.7))
.clipShape(Circle())
}
}
}
}
struct CameraControll: UIViewRepresentable {
@ObservedObject var camera: CameraViewModel
func makeUIView(context: Context) -> UIView { camera }
func updateUIView(_ uiView: UIViewType, context: Context) {}
}
class CameraViewModel: UIView, ObservableObject {
var previewLayer: AVCaptureVideoPreviewLayer?
var currentCameraPosition: AVCaptureDevice.Position = .front
var session = AVCaptureSession()
override func layoutSubviews() {
super.layoutSubviews()
_ = initCaptureSession
previewLayer?.frame = bounds
fixCameraOrientation()
}
lazy var initCaptureSession: Void = {
updateCameraPosition()
}()
private func updateCameraPosition() {
session.inputs.forEach { session.removeInput($0) }
guard let device = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera],
mediaType: .video,
position: .unspecified)
.devices.first(where: { $0.position == currentCameraPosition }),
let input = try? AVCaptureDeviceInput(device: device) else { return }
session.addInput(input)
if previewLayer == nil {
let previewLayer = AVCaptureVideoPreviewLayer(session: session)
layer.insertSublayer(previewLayer, at: 0)
self.previewLayer = previewLayer
}
session.startRunning()
}
func switchCamera() {
currentCameraPosition = currentCameraPosition == .front ? .back : .front
updateCameraPosition()
}
private func fixCameraOrientation() {
let rotation: CGFloat = CGFloat(getDeviceOrientation().rawValue) * 90
previewLayer?.transform = CATransform3DMakeRotation(rotation / 180 * CGFloat.pi, 0, 0, 1)
}
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
}
}
}
enum Orientation: Int {
case right = -1, down = 0, left = 1, up = 2
}
#Preview {
CameraView()
}
![[SwiftUI] Switching Between Front and Back Camera](https://cms.coiai.net/media/2024/11/JustFullScreenCam.webp)