fix: 修复扫码相机竞态并调整金额与浅色外观
串行处理相机配置及启停,补充四项生命周期回归测试。快捷金额去除多余小数,固定浅色模式,版本更新至 2.1.1(2010101)。
This commit is contained in:
@@ -10,11 +10,15 @@ import UIKit
|
||||
/// AVFoundation 二维码扫描页。
|
||||
final class QRCodeScannerViewController: BaseViewController, AVCaptureMetadataOutputObjectsDelegate {
|
||||
|
||||
/// 识别到有效二维码后返回内容,每次展示仅回调一次。
|
||||
var onScanResult: ((String) -> Void)?
|
||||
|
||||
private let captureSession = AVCaptureSession()
|
||||
private lazy var captureController = QRCodeCaptureSessionController(session: captureSession)
|
||||
private var previewLayer: AVCaptureVideoPreviewLayer?
|
||||
private var isCameraConfigured = false
|
||||
private var isCameraConfiguring = false
|
||||
private var isScannerVisible = false
|
||||
private var hasHandledResult = false
|
||||
|
||||
private let maskView = UIView()
|
||||
@@ -63,6 +67,7 @@ final class QRCodeScannerViewController: BaseViewController, AVCaptureMetadataOu
|
||||
|
||||
override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
isScannerVisible = true
|
||||
updatePreviewLayout()
|
||||
startCaptureSessionIfNeeded()
|
||||
}
|
||||
@@ -75,6 +80,7 @@ final class QRCodeScannerViewController: BaseViewController, AVCaptureMetadataOu
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
isScannerVisible = false
|
||||
stopCaptureSession()
|
||||
}
|
||||
|
||||
@@ -105,32 +111,44 @@ final class QRCodeScannerViewController: BaseViewController, AVCaptureMetadataOu
|
||||
return
|
||||
}
|
||||
|
||||
guard let device = AVCaptureDevice.default(for: .video),
|
||||
let input = try? AVCaptureDeviceInput(device: device) else {
|
||||
showToast("无法打开相机")
|
||||
return
|
||||
}
|
||||
guard !isCameraConfiguring else { return }
|
||||
isCameraConfiguring = true
|
||||
captureController.configure({ [weak self, captureSession] in
|
||||
guard let self,
|
||||
let device = AVCaptureDevice.default(for: .video),
|
||||
let input = try? AVCaptureDeviceInput(device: device),
|
||||
captureSession.canAddInput(input) else { return false }
|
||||
captureSession.addInput(input)
|
||||
|
||||
captureSession.beginConfiguration()
|
||||
defer { captureSession.commitConfiguration() }
|
||||
|
||||
guard captureSession.canAddInput(input) else { return }
|
||||
captureSession.addInput(input)
|
||||
|
||||
let output = AVCaptureMetadataOutput()
|
||||
guard captureSession.canAddOutput(output) else { return }
|
||||
captureSession.addOutput(output)
|
||||
output.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
|
||||
output.metadataObjectTypes = [.qr]
|
||||
|
||||
let layer = AVCaptureVideoPreviewLayer(session: captureSession)
|
||||
layer.videoGravity = .resizeAspectFill
|
||||
view.layer.insertSublayer(layer, at: 0)
|
||||
previewLayer = layer
|
||||
isCameraConfigured = true
|
||||
|
||||
updatePreviewLayout()
|
||||
startCaptureSessionIfNeeded()
|
||||
let output = AVCaptureMetadataOutput()
|
||||
guard captureSession.canAddOutput(output) else {
|
||||
captureSession.removeInput(input)
|
||||
return false
|
||||
}
|
||||
captureSession.addOutput(output)
|
||||
guard output.availableMetadataObjectTypes.contains(.qr) else {
|
||||
captureSession.removeOutput(output)
|
||||
captureSession.removeInput(input)
|
||||
return false
|
||||
}
|
||||
output.setMetadataObjectsDelegate(self, queue: .main)
|
||||
output.metadataObjectTypes = [.qr]
|
||||
return true
|
||||
}, completion: { [weak self] succeeded in
|
||||
guard let self else { return }
|
||||
self.isCameraConfiguring = false
|
||||
guard succeeded else {
|
||||
self.showToast("无法打开相机")
|
||||
return
|
||||
}
|
||||
let layer = AVCaptureVideoPreviewLayer(session: self.captureSession)
|
||||
layer.videoGravity = .resizeAspectFill
|
||||
self.view.layer.insertSublayer(layer, at: 0)
|
||||
self.previewLayer = layer
|
||||
self.isCameraConfigured = true
|
||||
self.updatePreviewLayout()
|
||||
self.startCaptureSessionIfNeeded()
|
||||
})
|
||||
}
|
||||
|
||||
private func updatePreviewLayout() {
|
||||
@@ -138,17 +156,12 @@ final class QRCodeScannerViewController: BaseViewController, AVCaptureMetadataOu
|
||||
}
|
||||
|
||||
private func startCaptureSessionIfNeeded() {
|
||||
guard isCameraConfigured, !captureSession.isRunning else { return }
|
||||
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
|
||||
self?.captureSession.startRunning()
|
||||
}
|
||||
guard isScannerVisible, isCameraConfigured, !hasHandledResult else { return }
|
||||
captureController.setRunning(true)
|
||||
}
|
||||
|
||||
private func stopCaptureSession() {
|
||||
guard captureSession.isRunning else { return }
|
||||
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
|
||||
self?.captureSession.stopRunning()
|
||||
}
|
||||
captureController.setRunning(false)
|
||||
}
|
||||
|
||||
private func updateMask() {
|
||||
|
||||
Reference in New Issue
Block a user