SwiftUI ——扫码

权限配置
在Info.plist文件中配置相机权限 Privacy - Camera Usage Description

SwiftUI ——扫码_第1张图片

创建一个类,实现了AVCaptureMetadataOutputObjectsDelegate 协议,用于处理扫描到的元数据对象;并做权限处理

//
//  ScannerViewModel.swift
//
//  Created by 123 on 2023/10/25.
//

import Foundation
import AVFoundation
import SwiftUI

class ScannerViewModel: NSObject,ObservableObject, AVCaptureMetadataOutputObjectsDelegate {
    var scanSession:AVCaptureSession!
    var previewLayer: AVCaptureVideoPreviewLayer?
    @Published var lastQrCode: String = ""
    var scanPermission = 0
    // 初始设置
    func prepareScan() {
        do {
            self.scanSession = AVCaptureSession()
            self.previewLayer = AVCaptureVideoPreviewLayer(session: self.scanSession)
            self.previewLayer?.videoGravity = .resizeAspectFill
            guard let videoCaptureDevice = AVCaptureDevice.default(for: .video)
            else { return }
            
            let videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)
            
            if (self.scanSession.canAddInput(videoInput)) {
                self.scanSession.addInput(videoInput)
            } else {
                return
            }
            
            let metadataOutput = AVCaptureMetadataOutput()
            
            if (self.scanSession.canAddOutput(metadataOutput)) {
                self.scanSession.addOutput(metadataOutput)
                
                metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
                metadataOutput.metadataObjectTypes = [.qr]
            } else {
                print("Could not add Metadata output.")
                return
            }
            
        } catch {
            print("Failed to initialize scanner.")
        }
    }
    func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
        self.scanSession.stopRunning()
        
        if let metadataObject = metadataObjects.first {
            guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject else { return }
            guard let stringValue = readableObject.stringValue else { return }
            lastQrCode = stringValue
            // 这里可以获取扫描到的二维码内容
            print("QR Code Scanned: \(stringValue)")
        }
    }
    func showPermissionDeniedAlert() {
        guard let rootViewController = UIApplication.shared.windows.filter({$0.isKeyWindow}).first?.rootViewController else {
            return
        }
        
        let alertController = UIAlertController(title: "相机权限",
                                                message: "二维码扫描需要访问相机。请在设置中启用相机权限。",
                                                preferredStyle: .alert)
        
        alertController.addAction(UIAlertAction(title: "取消", style: .default) {_ in
            self.scanPermission = 2
        })
        
        alertController.addAction(UIAlertAction(title: "设置", style: .default) { _ in
            if let settingsUrl = URL(string: UIApplication.openSettingsURLString) {
                if UIApplication.shared.canOpenURL(settingsUrl) {
                    UIApplication.shared.open(settingsUrl)
                }
            }
        })
        
        rootViewController.present(alertController, animated: true, completion: nil)
    }
    // 函数来检查权限并进行适当处理
    func checkPermissions() {
        switch AVCaptureDevice.authorizationStatus(for: .video) {
        case .authorized: // 用户已经授权
            self.prepareScan() // 继续设置扫描
            self.scanPermission = 1
            
        case .notDetermined: // 用户还没有决定
            // 请求权限
            AVCaptureDevice.requestAccess(for: .video) { granted in
                if granted {
                    DispatchQueue.main.async {
                        self.prepareScan() // 如果被授权,再次调用准备扫描的方法
                        self.scanPermission = 1
                    }
                }else{
                    // 如果不允许,则可以在这里处理,如弹出界面提示用户
                    self.scanPermission = 2
                }
                
            }
        case .denied, .restricted: // 用户被禁用或在家长控制下限制
            if(scanPermission != 2){
                // 弹出窗口告诉用户他们没有权限
                showPermissionDeniedAlert()
            }
            
       
            print("Camera permissions are either denied or restricted.")
        @unknown default:
            fatalError("Unknown status of camera authorization.")
        }
        
    }
}

2.实现UIViewRepresentable,展示扫描视频流 

//
//  QRScannerView.swift
//实现 UIViewRepresentable 协议,这是一个用于在 SwiftUI 中嵌入 UIKit 视图的抽象。结构体中包含一个 @ObservedObject 修饰的 ScannerViewModel 实例,使得视图可以对 ViewModel 的更改做出响应。
//  Created by 123 on 2023/10/25.
//

import Foundation
import SwiftUI
import AVFoundation
struct QRScannerView: UIViewRepresentable {
    @ObservedObject var scannerViewModel: ScannerViewModel
    func makeUIView(context: Context) ->  UIView {
        let view = UIView(frame: CGRect.zero)
        
        DispatchQueue.main.async {
            //               调用 scannerViewModel.checkPermissions() 检查相机权限。
            self.scannerViewModel.checkPermissions()
            //               设置 preview layer 的 frame 为新 UIView 实例的边界,并将其添加到 UIView 的 layer 上。显示来自摄像头的视频流
            if let previewLayer = self.scannerViewModel.previewLayer {
                previewLayer.frame = view.layer.bounds
                view.layer.addSublayer(previewLayer)
            }
            if(self.scannerViewModel.scanPermission==1){
                self.scannerViewModel.scanSession.startRunning()
            }
            
        }
        return view
    }
    
    func updateUIView(_ view: UIView, context: Context) {
        if let previewLayer = scannerViewModel.previewLayer {
            previewLayer.frame = view.layer.bounds
        }
    }
    
}

3.扫码页面

struct ScannerView:View{
    @StateObject var scannerViewModel = ScannerViewModel()
    let meetingViewModel:MeetingViewModel
    var body: some View {
        ZStack {
            QRScannerView(scannerViewModel: scannerViewModel)
           
          
            if scannerViewModel.lastQrCode != "" {
                Text("Last scanned QR Code: \(scannerViewModel.lastQrCode)")
                    .foregroundColor(.white)
                    .padding()
                    .background(Color.black.opacity(0.7))
                    }
            }
            
            
        }
    }
}

你可能感兴趣的:(ios(swiftUI),开发,swiftui,ios,swift)