Files
Thijooree-iOS/Thijooree iOS/Views/Shared/QRScannerSheet.swift
T

87 lines
2.9 KiB
Swift

import SwiftUI
import AVFoundation
struct QRScannerSheet: View {
var onResult: (String) -> Void
@Environment(\.dismiss) private var dismiss
var body: some View {
NavigationStack {
QRCameraView(onResult: { result in
dismiss()
onResult(result)
})
.ignoresSafeArea()
.navigationTitle("Scan QR Code")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button("Cancel") { dismiss() }
.foregroundStyle(.white)
}
}
}
}
}
private struct QRCameraView: UIViewRepresentable {
var onResult: (String) -> Void
func makeUIView(context: Context) -> UIView {
let view = UIView()
view.backgroundColor = .black
let session = AVCaptureSession()
guard let device = AVCaptureDevice.default(for: .video),
let input = try? AVCaptureDeviceInput(device: device) else {
return view
}
if session.canAddInput(input) { session.addInput(input) }
let output = AVCaptureMetadataOutput()
if session.canAddOutput(output) {
session.addOutput(output)
output.setMetadataObjectsDelegate(context.coordinator, queue: .main)
output.metadataObjectTypes = [.qr]
}
let preview = AVCaptureVideoPreviewLayer(session: session)
preview.videoGravity = .resizeAspectFill
preview.frame = view.bounds
view.layer.addSublayer(preview)
context.coordinator.previewLayer = preview
DispatchQueue.global(qos: .userInitiated).async { session.startRunning() }
context.coordinator.session = session
return view
}
func updateUIView(_ uiView: UIView, context: Context) {
context.coordinator.previewLayer?.frame = uiView.bounds
}
func makeCoordinator() -> Coordinator { Coordinator(onResult: onResult) }
final class Coordinator: NSObject, AVCaptureMetadataOutputObjectsDelegate {
var onResult: (String) -> Void
var session: AVCaptureSession?
var previewLayer: AVCaptureVideoPreviewLayer?
private var didCapture = false
init(onResult: @escaping (String) -> Void) {
self.onResult = onResult
}
func metadataOutput(_ output: AVCaptureMetadataOutput,
didOutput objects: [AVMetadataObject],
from connection: AVCaptureConnection) {
guard !didCapture,
let obj = objects.first as? AVMetadataMachineReadableCodeObject,
let str = obj.stringValue else { return }
didCapture = true
session?.stopRunning()
DispatchQueue.main.async { self.onResult(str) }
}
}
}