扫描条形码
1、iOS10之后 开启手机的摄像头需要在 Info.plist 里面添加两个键值对
1 Privacy - Photo Library Usage Description 2 Privacy - Camera Usage Description
2、 导入模块 AVFoundation、 遵守 AVCaptureMetadataOutputObjectsDelegate 协议
3、需要真机调用摄像头,不能用模拟器进行测试
代码:
1 import UIKit 2 import AVFoundation 3 4 class ViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate { 5 6 var scanRectView: UIView! 7 var scanLine: UIImageView! 8 var timer: Timer! 9 10 var device: AVCaptureDevice! 11 var input: AVCaptureDeviceInput! 12 var output: AVCaptureMetadataOutput! 13 var session: AVCaptureSession! 14 var preview: AVCaptureVideoPreviewLayer! 15 16 // 摄像头按钮 17 lazy var cameraBtn: UIButton = { 18 let cameraBtn = UIButton(frame: CGRect(x: 10, y: self.view.frame.height * 0.8, width: self.view.frame.width - 20, height: 44)) 19 cameraBtn.setTitle("扫一扫", for: .normal) 20 cameraBtn.setTitleColor(UIColor.orange, for: .normal) 21 cameraBtn.backgroundColor = UIColor.gray 22 cameraBtn.addTarget(self, action: #selector(ViewController.fromCamera), for: .touchUpInside) 23 24 return cameraBtn 25 }() 26 27 override func viewDidLoad() { 28 super.viewDidLoad() 29 30 self.view.addSubview(self.cameraBtn) 31 } 32 33 // MARK: -life cycle 34 35 // 通过摄像头扫描 36 func fromCamera() { 37 do { 38 self.device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo) 39 40 self.input = try AVCaptureDeviceInput(device: device) 41 42 self.output = AVCaptureMetadataOutput() 43 output.setMetadataObjectsDelegate(self, queue: DispatchQueue.main) 44 45 self.session = AVCaptureSession() 46 if UIScreen.main.bounds.size.height < 500 { 47 self.session.sessionPreset = AVCaptureSessionPreset640x480 48 } else { 49 self.session.sessionPreset = AVCaptureSessionPresetHigh 50 } 51 52 self.session.addInput(self.input) 53 self.session.addOutput(self.output) 54 55 self.output.metadataObjectTypes = [AVMetadataObjectTypeEAN8Code, 56 AVMetadataObjectTypeEAN13Code, 57 AVMetadataObjectTypeCode128Code, 58 AVMetadataObjectTypeCode93Code] 59 60 // 计算中间可探测区域 61 let windowSize = UIScreen.main.bounds.size 62 let scanSize = CGSize(width: windowSize.width * 3 / 4, height: windowSize.height * 3 / 4) 63 var scanRect = CGRect(x: (windowSize.width - scanSize.width) / 2, 64 y: (windowSize.height - scanSize.height) / 2, 65 width: scanSize.width, 66 height: scanSize.height) 67 // 计算 rectOfInterset 注意x,y 交换位置 68 scanRect = CGRect(x: scanRect.origin.y / windowSize.height, 69 y: scanRect.origin.x / windowSize.width, width: scanRect.size.height / windowSize.height, height: scanRect.size.width / windowSize.width) 70 // 设置可探测区域 71 self.output.rectOfInterest = scanRect 72 73 self.preview = AVCaptureVideoPreviewLayer(session: self.session) 74 self.preview.videoGravity = AVLayerVideoGravityResizeAspectFill 75 self.preview.frame = UIScreen.main.bounds 76 self.view.layer.insertSublayer(self.preview, at: 0) 77 78 // 添加中间的探测区域绿框 79 self.scanRectView = UIView() 80 self.view.addSubview(self.scanRectView) 81 self.scanRectView.frame = CGRect(x: 0, y: 0, width: scanSize.width, height: 100) 82 print(self.scanRectView.frame) 83 self.scanRectView.center = CGPoint(x: UIScreen.main.bounds.midX, y: UIScreen.main.bounds.midY) 84 self.scanRectView.layer.borderColor = UIColor.green.cgColor 85 self.scanRectView.layer.borderWidth = 1 86 87 // 添加扫描线 88 self.scanLine = UIImageView() 89 self.scanLine.frame = CGRect(x: 0, y: 0, width: self.scanRectView.frame.size.width, height: 5) 90 scanLine.image = UIImage(named: "line3") 91 // 添加扫描线图层 92 self.scanRectView.addSubview(self.scanLine) 93 // 创建定时器, 用于实现扫描线动画 94 self.timer = Timer() 95 timer = Timer.scheduledTimer(timeInterval: 4, target: self, selector: #selector(ViewController.moveScannerLayer(_:)), userInfo: nil, repeats: true) 96 97 98 // 开始捕获 99 self.session.startRunning() 100 101 // 放大 102 do { 103 try self.device.lockForConfiguration() 104 } catch _ { 105 print("Error: lock For Cpnfuguration.") 106 } 107 self.device.videoZoomFactor = 1.5 108 self.device.unlockForConfiguration() 109 } catch _ { 110 // 打印错误信息 111 // 打印错误消息 112 let alertControl = UIAlertController(title: "提醒", message: "请在 iPhone 的\"设置-隐私-相机\"选项中,允许本程序访问您的相机", preferredStyle: .alert) 113 let cancelAction = UIAlertAction(title: "确定", style: .cancel, handler: nil) 114 alertControl.addAction(cancelAction) 115 self.present(alertControl, animated: true, completion: nil) 116 } 117 } 118 119 // 让扫描线滚动 120 func moveScannerLayer(_ timer: Timer) { 121 self.scanLine.frame = CGRect(x: 0, y: 0, width: self.scanRectView.frame.size.width, height: 12) 122 UIView.animate(withDuration: 2) { 123 self.scanLine.frame = CGRect(x: self.scanLine.frame.origin.x, 124 y: self.scanLine.frame.origin.y + self.scanRectView.frame.size.height - 10, 125 width: self.scanLine.frame.width, 126 height: self.scanLine.frame.height) 127 } 128 } 129 130 131 // MARK: -AVCaptureMetadataOutputObjectsDelegate 132 // 摄像头捕获 133 func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) { 134 135 var stringValue: String? 136 if metadataObjects.count > 0 { 137 let metadataObject = metadataObjects[0] as! AVMetadataMachineReadableCodeObject 138 stringValue = metadataObject.stringValue 139 if stringValue != nil { 140 self.session.stopRunning() 141 } 142 } 143 144 self.session.stopRunning() 145 // 输出结果 146 let alertController = UIAlertController(title: "二维码", message: stringValue, preferredStyle: .alert) 147 let okAction = UIAlertAction(title: "确定", style: .default) { (action) in 148 // 继续扫描 149 self.session.startRunning() 150 } 151 alertController.addAction(okAction) 152 self.present(alertController, animated: true, completion: nil) 153 } 154 155 156 157 override func didReceiveMemoryWarning() { 158 super.didReceiveMemoryWarning() 159 // Dispose of any resources that can be recreated. 160 } 161 162 163 }