其他分享
首页 > 其他分享> > iOS 扫一扫功能

iOS 扫一扫功能

作者:互联网

class TestScanView: UIView {
    ///扫描结果
    var scanBlock:((AVMetadataObject.ObjectType,String)->())?
    
    let middle_width = kScreenWidth * 0.8
    let top_height = kScreenHeight * 0.25
    let center_line_height:CGFloat = 10
    
    lazy var centerLineView: UIImageView = {
        let view = UIImageView(frame: CGRect(x: (kScreenWidth - middle_width)/2, y: top_height, width: middle_width, height: center_line_height * middle_width/267))
        view.image = UIImage(named: "scan_center_line")
        return view
    }()
    
    lazy var blackMaskView: UIImageView = {
        let view = UIImageView(frame: kWindow.bounds)
        view.image = maskViewImage(maskRect: kWindow.bounds, clearRect: CGRect(x: (kScreenWidth - middle_width)/2, y: top_height, width: middle_width, height: middle_width))
        return view
    }()
    
    lazy var borderView: UIImageView = {
        let view = UIImageView(frame:CGRect(x: (kScreenWidth - middle_width)/2, y: top_height, width: middle_width, height: middle_width))
        view.image = UIImage(named: "scan_border_line")
        return view
    }()
    
    lazy var device: AVCaptureDevice? = {
        let d = AVCaptureDevice.default(for: .video)
        return d
    }()
    
    lazy var scannerSession: AVCaptureSession = {
        let session = AVCaptureSession()
        guard device != nil else {
            kWindow.makeToast("相机不可用", duration: 1.5, position: .center)
            return session
        }
        
        let input = try? AVCaptureDeviceInput(device: device!)
        let output = AVCaptureMetadataOutput()
        output.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
        //设置扫描的区域
        NotificationCenter.default.addObserver(forName: Notification.Name.AVCaptureInputPortFormatDescriptionDidChange, object: nil, queue: OperationQueue.current) { [self] _ in
            output.rectOfInterest = previewLayer.metadataOutputRectConverted(fromLayerRect: CGRect(x: (kScreenWidth - middle_width)/2, y: top_height, width: middle_width, height: middle_width))
        }
        
        if session.canSetSessionPreset(.hd1280x720) {
            session.sessionPreset = .hd1280x720
        }
        else if session.canSetSessionPreset(.hd1920x1080) {
            session.sessionPreset = .hd1920x1080
        }
        else if session.canSetSessionPreset(.hd4K3840x2160) {
            session.sessionPreset = .hd4K3840x2160
        }
        else {
            session.sessionPreset = .photo
        }
        
        guard input != nil else {
            kWindow.makeToast("相机不可用", duration: 1.5, position: .center)
            return session
        }
        
        if session.canAddInput(input!) {
            session.addInput(input!)
        }
        if session.canAddOutput(output) {
            session.addOutput(output)
        }
        output.metadataObjectTypes = [.qr,.code128,.ean8,.upce,.code39,.pdf417,.aztec,.code93,.ean13,.code39Mod43]
        return session
    }()
    
    lazy var previewLayer: AVCaptureVideoPreviewLayer = {
        let layer = AVCaptureVideoPreviewLayer(session: scannerSession)
        layer.videoGravity = .resizeAspectFill
        layer.frame = bounds
        return layer
    }()
    ///判断centerLineView是往下走还是往上走
    var isGoDown = true
    var timer:Timer?
    /// centerLineView实时的位置增量
    var updateMeasure = 0
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        layer.insertSublayer(previewLayer, at: 0)
        addSubview(blackMaskView)
        addSubview(borderView)
        addSubview(centerLineView)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    //MARK: 自动去聚焦
    func autoFocus() {
        guard device != nil else {
            return
        }
        scannerSession.beginConfiguration()
        try? device!.lockForConfiguration()
        if device!.isFocusModeSupported(.autoFocus) {
            device!.focusMode = .autoFocus
        }
        if device!.isExposureModeSupported(.autoExpose) {
            device!.exposureMode = .autoExpose
        }
        device!.unlockForConfiguration()
        scannerSession.commitConfiguration()
    }
    
    func maskViewImage(maskRect:CGRect,clearRect:CGRect) -> UIImage? {
        UIGraphicsBeginImageContext(maskRect.size)
        let ctx = UIGraphicsGetCurrentContext()!
        ctx.setFillColor(red: 0,green: 0,blue: 0,alpha: 0.6)
        var drawRect = maskRect
        
        ctx.fill(drawRect)   //draw the transparent layer
        
        drawRect = clearRect
        ctx.clear(drawRect)  //clear the center rect  of the layer
        
        let returnImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return returnImage
    }
    //MARK: 开启扫描
    func startSession() {
        if timer != nil {
            timer?.invalidate()
            timer = nil
        }
        timer = Timer.scheduledTimer(timeInterval: 0.02, target: self, selector: #selector(animation), userInfo: nil, repeats: true)
        scannerSession.startRunning()
    }
    //MARK: 关闭扫描
    func stopSession() {
        timer?.invalidate()
        timer = nil
        flashSettingAction(isOpen: false)
        scannerSession.stopRunning()
    }
    //MARK: 横线移动
    @objc func animation() {
        let originalX = (kScreenWidth - middle_width)/2
        if isGoDown {
            updateMeasure += 1
            centerLineView.frame = CGRect(x: originalX, y: top_height + 2 * CGFloat(updateMeasure), width: middle_width, height: center_line_height * middle_width/267)
            if 2 * CGFloat(updateMeasure) > middle_width -  center_line_height * middle_width/267 {
                isGoDown = false
            }
        }
        else {
            updateMeasure -= 1
            centerLineView.frame = CGRect(x: originalX, y: top_height + 2 * CGFloat(updateMeasure), width: middle_width, height: center_line_height * middle_width/267)
            if updateMeasure == 0 {
                isGoDown = true
            }
        }
        autoFocus()
    }
    //MARK: 开启关闭闪光灯
    func flashSettingAction(isOpen:Bool) {
        guard device != nil else {
            return
        }
        
        if device!.hasTorch {
            try? device!.lockForConfiguration()
            device!.torchMode = isOpen ? .on : .off
            device!.unlockForConfiguration()
        }
        
    }
}

extension TestScanView: AVCaptureMetadataOutputObjectsDelegate {
    func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
        print(metadataObjects)
        if metadataObjects.count > 0 {
            stopSession()
            if let scanResult = metadataObjects.first as? AVMetadataMachineReadableCodeObject {
                if scanResult.stringValue != nil {
                    print("扫描类型",scanResult.type)
                    print("扫描结果",scanResult.stringValue!)
                    scanBlock?(scanResult.type,scanResult.stringValue!)
                }
                else {
                    startSession()
                }
               
            }
            else {
                startSession()
            }
        }
        else {
            startSession()
        }
    }
}

标签:功能,middle,扫一扫,iOS,height,width,session,let,device
来源: https://blog.csdn.net/weixin_43259805/article/details/122410646