出现视图时重新启动相机

问题描述 投票:0回答:1

我的应用程序出现问题。我正在尝试在视图内构建条形码读取器。当我第一次启动视图时,阅读器工作正常,但如果我切换到另一个视图,然后返回到上一个视图(条形码所在的位置),我的相机就会停止工作。我想让它在每次切换回条形码所在的视图时都能正常工作。

这是我的代码:

import AVKit
import Foundation
import SwiftUI
import VisionKit

enum ScanType: String {
    case barcode, text
}

enum DataScannerAccessStatusType {
    case notDetermined
    case cameraAccessNotGranted
    case cameraNotAvailable
    case scannerAvailable
    case scannerNotAvailable
}

@MainActor
final class ScannerDataViewModel: ObservableObject {
    
    @State private var session: AVCaptureSession = .init()
    @Published var dataScannerAccessStatus: DataScannerAccessStatusType = .notDetermined
    @Published var recognizedItems: [RecognizedItem] = []
    @Published var scanType: ScanType = .barcode
    @Published var textContentType: DataScannerViewController.TextContentType?
    @Published var recognizesMultipleItems = true
    
    var recognizedDataType: DataScannerViewController.RecognizedDataType {
        scanType == .barcode ? .barcode() : .text(textContentType: textContentType)
    }
    
    var headerText: String {
        if recognizedItems.isEmpty {
            return "Scansione \(scanType.rawValue)"
        } else {
            return "Riconosciuti \(recognizedItems.count) oggetti"
        }
    }
    
      var dataScannerViewId: Int {
        var hasher = Hasher()
        hasher.combine(scanType)
        hasher.combine(recognizesMultipleItems)
        if let textContentType {
            hasher.combine(textContentType)
        }
        return hasher.finalize()
    }
    
    private var isScannerAvailable: Bool {
        DataScannerViewController.isAvailable && DataScannerViewController.isSupported
    }
    
    func requestDataScannerAccessStatus() async {
        guard UIImagePickerController.isSourceTypeAvailable(.camera) else {
            dataScannerAccessStatus = .cameraNotAvailable
            return
        }
        
        switch AVCaptureDevice.authorizationStatus(for: .video) {
            
        case .authorized:
            dataScannerAccessStatus = isScannerAvailable ? .scannerAvailable : .scannerNotAvailable
            
        case .restricted, .denied:
            dataScannerAccessStatus = .cameraAccessNotGranted
            
        case .notDetermined:
            let granted = await AVCaptureDevice.requestAccess(for: .video)
            if granted {
                dataScannerAccessStatus = isScannerAvailable ? .scannerAvailable : .scannerNotAvailable
            } else {
                dataScannerAccessStatus = .cameraAccessNotGranted
            }
        
        default: break
            
        }
    }
}

这是 DataScannerView:

import Foundation
import SwiftUI
import VisionKit

struct DataScannerView: UIViewControllerRepresentable {
    
    @Binding var recognizedItems: [RecognizedItem]
    let recognizedDataType: DataScannerViewController.RecognizedDataType
    let recognizesMultipleItems: Bool
    
    
    func makeUIViewController(context: Context) -> DataScannerViewController {
        let vc = DataScannerViewController(
            recognizedDataTypes: [recognizedDataType],
            qualityLevel: .balanced,
            recognizesMultipleItems: recognizesMultipleItems,
            isGuidanceEnabled: true,
            isHighlightingEnabled: true
        )
        return vc
    }
    
    func updateUIViewController(_ uiViewController: DataScannerViewController, context: Context) {
        uiViewController.delegate = context.coordinator
        try? uiViewController.startScanning()
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(recognizedItems: $recognizedItems)
    }
    
    static func dismantleUIViewController(_ uiViewController: DataScannerViewController, coordinator: Coordinator) {
        uiViewController.stopScanning()
    }
    
    
    class Coordinator: NSObject, DataScannerViewControllerDelegate {
        
        @Binding var recognizedItems: [RecognizedItem]

        init(recognizedItems: Binding<[RecognizedItem]>) {
            self._recognizedItems = recognizedItems
        }
        
        func dataScanner(_ dataScanner: DataScannerViewController, didTapOn item: RecognizedItem) {
            print("didTapOn \(item)")
        }
        
        func dataScanner(_ dataScanner: DataScannerViewController, didAdd addedItems: [RecognizedItem], allItems: [RecognizedItem]) {
            UINotificationFeedbackGenerator().notificationOccurred(.success)
            recognizedItems.append(contentsOf: addedItems)
            print("didAddItems \(addedItems)")
        }
        
        func dataScanner(_ dataScanner: DataScannerViewController, didRemove removedItems: [RecognizedItem], allItems: [RecognizedItem]) {
            self.recognizedItems = recognizedItems.filter { item in
                !removedItems.contains(where: {$0.id == item.id })
            }
            print("didRemovedItems \(removedItems)")
        }
        
        func dataScanner(_ dataScanner: DataScannerViewController, becameUnavailableWithError error: DataScannerViewController.ScanningUnavailable) {
            print("became unavailable with error \(error.localizedDescription)")
        }
        
    }
    
}

最后,这是我想展示我的条形码阅读器的视图:

import SwiftUI

struct ScanView: View {
    
    @Environment(\.presentationMode) var presentationMode
    @StateObject private var vm: ScannerDataViewModel = ScannerDataViewModel()
    private let viewInsideScan = ViewInsideScanView()
    
    var body: some View {
        NavigationView {
            ZStack {
                Color("whiteBackground").ignoresSafeArea()
                VStack(spacing: 8) {
                    
                    Text("Posiziona il codice a barre al centro")
                        .font(.title3)
                        .foregroundColor(Color.primary)
                        .padding(.top, 20)
                    
                    Text("La scansione inizierà automaticamente")
                        .font(.callout)
                        .foregroundColor(Color.primary)
                    
                    Spacer()
                    
                    CenteredSquareView(content: viewInsideScan)
                        .frame(width: 350, height: 350, alignment: .center)
                    
                    Spacer()
                    
                    Image(systemName: "qrcode.viewfinder")
                        .font(.largeTitle)
                        .foregroundColor(.gray)
                    
                    
                    Text("Tocca l'icona per eseguire una nuova scansione")
                        .font(.callout)
                        .foregroundColor(Color.primary)
                    
                    Spacer(minLength: 45)
                    
                }
            }
            .environmentObject(vm)
            .onAppear {
                Task {
                    await vm.requestDataScannerAccessStatus()
                }
            }
        }
    }
}

struct CenteredSquareView<Content: View>: View {
    var content: Content
    
    var body: some View {
        GeometryReader { geometry in
            ZStack {
                content
                    .frame(width: geometry.size.width * 1, height: geometry.size.width * 1)
                    .cornerRadius(20)
                    .position(x: geometry.size.width / 2, y: geometry.size.height / 2) // Posiziona al centro
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}

#Preview {
    ScanView()
}

我知道,这太令人困惑了。我是一名初级开发人员,我正在努力变得更好。我注意到,每次我切换到阅读器所在的视图时,我的相机都会工作几秒钟,然后停止。 谢谢大家的帮助!

swiftui camera reader
1个回答
0
投票

看来该问题可能与 ScanView 中使用 DataScannerView 的方式有关。具体来说,当您切换到另一个视图然后返回时,将重新创建 DataScannerView,并且它可能无法正确处理返回到视图的转换。

尝试修改 DataScannerView 以更明确地处理扫描会话的启动和停止。您可以通过使用 onAppear 和 onDisappear 修饰符在视图出现和消失时启动和停止扫描会话来完成此操作。

在 GitHub 中分享您的项目以进行检查。

© www.soinside.com 2019 - 2024. All rights reserved.