import SwiftUI
import AVFoundation
import UIKit
struct CameraView: View {
@StateObject private var camera = CameraModel()
@Environment(\.dismiss) private var dismiss
let onImageCaptured: (UIImage) -> Void
var body: some View {
ZStack {
CameraPreview(camera: camera)
.ignoresSafeArea()
VStack {
HStack {
Button(action: {
dismiss()
}) {
Image(systemName: "xmark.circle.fill")
.font(.largeTitle)
.foregroundColor(.white)
.background(Color.black.opacity(0.5))
.clipShape(Circle())
}
.padding()
Spacer()
}
Spacer()
HStack {
Spacer()
// Capture Button
Button(action: {
camera.takePhoto { image in
onImageCaptured(image)
}
}) {
ZStack {
Circle()
.fill(Color.white)
.frame(width: 70, height: 70)
Circle()
.stroke(Color.white, lineWidth: 4)
.frame(width: 80, height: 80)
}
}
Spacer()
}
.padding(.bottom, 30)
}
}
.onAppear {
camera.checkPermission()
}
}
}
struct CameraPreview: UIViewRepresentable {
@ObservedObject var camera: CameraModel
func makeUIView(context: Context) -> UIView {
let view = UIView(frame: UIScreen.main.bounds)
camera.preview = AVCaptureVideoPreviewLayer(session: camera.session)
camera.preview.frame = view.frame
camera.preview.videoGravity = .resizeAspectFill
view.layer.addSublayer(camera.preview)
camera.session.startRunning()
return view
}
func updateUIView(_ uiView: UIView, context: Context) {
}
}
class CameraModel: NSObject, ObservableObject, AVCapturePhotoCaptureDelegate {
@Published var session = AVCaptureSession()
@Published var alert = false
@Published var output = AVCapturePhotoOutput()
@Published var preview: AVCaptureVideoPreviewLayer!
private var photoCompletion: ((UIImage) -> Void)?
func checkPermission() {
switch AVCaptureDevice.authorizationStatus(for: .video) {
case .authorized:
setUp()
return
case .notDetermined:
AVCaptureDevice.requestAccess(for: .video) { status in
if status {
self.setUp()
}
}
case .denied:
self.alert.toggle()
return
default:
return
}
}
func setUp() {
do {
self.session.beginConfiguration()
guard let device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else {
return
}
let input = try AVCaptureDeviceInput(device: device)
if self.session.canAddInput(input) {
self.session.addInput(input)
}
if self.session.canAddOutput(self.output) {
self.session.addOutput(self.output)
}
self.session.commitConfiguration()
} catch {
print(error.localizedDescription)
}
}
func takePhoto(completion: @escaping (UIImage) -> Void) {
self.photoCompletion = completion
DispatchQueue.global(qos: .background).async {
self.output.capturePhoto(with: AVCapturePhotoSettings(), delegate: self)
}
}
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
if let error = error {
print("Error capturing photo: \(error)")
return
}
guard let imageData = photo.fileDataRepresentation(),
let image = UIImage(data: imageData) else {
return
}
DispatchQueue.main.async {
self.photoCompletion?(image)
}
}
}