杨锴
2024-10-09 1f08239dd60c05f1eb6eb9b3122aa23cd700348b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
//
//  CommonScanQRCodeVC.swift
//  WanPai
//
//  Created by 无故事王国 on 2023/9/11.
//
 
import UIKit
import AVKit
import JQTools
import SceneKit
 
class CommonScanQRCodeVC: BaseVC,AVCaptureMetadataOutputObjectsDelegate {
 
                //是否是第一次扫描到结果
                public var firstResult: Bool = true
                //AVCaptureSession对象来执行输入设备和输出设备之间的数据传递
                public var session: AVCaptureSession?
                //输入设备
                public var videoInput: AVCaptureDeviceInput?
                //输出对象
                public var metadataOutput: AVCaptureMetadataOutput = AVCaptureMetadataOutput()
                //预览图层
                public var previewLayer: AVCaptureVideoPreviewLayer?
                public var scanTimer: Timer?
                //边框
                public var borderView:UIImageView?
                //line
                public var scanLineView:UIImageView?
                //返回按钮
                public var closeBtn:UIButton?
 
                private let borderWidth:CGFloat =  400
                private let topMargin: CGFloat =  230
                private var clouse:((String,Bool)->Void)?
 
                /// 返回扫描结果,是否时第一次扫描到结果
                public convenience init(clouse:@escaping (String,Bool)->Void){
                                self.init()
                                self.clouse = clouse
                }
 
                public override func viewDidLoad() {
                                super.viewDidLoad()
                                title = "扫描二维码"
                                if UIImagePickerController.isSourceTypeAvailable(.camera){
 
                                                if borderView == nil {
                                                                borderView = UIImageView(image: UIImage(named: "ty_qrcode_bg"))
                                                }
 
                                                if scanLineView == nil {
                                                                scanLineView = UIImageView(image: UIImage(named: "ty_qrcode_line"))
                                                }
 
                                                if closeBtn == nil &&  modalPresentationStyle == .fullScreen && self.presentationController != nil {
                                                                closeBtn = UIButton(type: .custom)
                                                                closeBtn!.addTarget(self, action: #selector(closeView), for: .touchUpInside)
                                                                let image = Bundle.JQ_Bundle(icon: "close_btn")
                                                                closeBtn!.setImage(image, for: .normal)
                                                                view.addSubview(closeBtn!)
                                                                closeBtn!.snp.makeConstraints { (make) in
                                                                                make.top.equalTo(30)
                                                                                make.left.equalTo(20)
                                                                                make.width.height.equalTo(30)
                                                                }
                                                }
 
                                                let authStatus = AVCaptureDevice.authorizationStatus(for: AVMediaType.video)
                                                if authStatus == AVAuthorizationStatus.notDetermined {
                                                                AVCaptureDevice.requestAccess(for: AVMediaType.video) { (granted) in
                                                                                if granted{
                                                                                                self.limitSuccess()
                                                                                }else{
                                                                                                self.limitFaild()
                                                                                }
                                                                }
                                                } else if authStatus == AVAuthorizationStatus.restricted || authStatus == AVAuthorizationStatus.denied {
                                                                limitFaild()
                                                } else {
                                                                limitSuccess()
                                                }
                                }else{
                                                alertError(msg: "当前环境相机不可用")
                                                DispatchQueue.main.asyncAfter(deadline: .now()+1) {
                                                                self.navigationController?.popViewController()
                                                }
                                }
                }
 
                private func limitSuccess(){
                                setupCaptureSession()
                                DispatchQueue.main.async {
                                                self.addSubviews()
                                                self.scanTimer = Timer.scheduledTimer(timeInterval: 3.0, target: self, selector: #selector(self.startAnimation), userInfo: nil, repeats: true)
 
                                                self.firstResult = true
                                                if let session = self.session {
                                                                let queue = DispatchQueue(label: "startRun")
                                                                queue.async {
                                                                                session.startRunning()
                                                                }
                                                }
                                                self.scanTimer?.fireDate = NSDate.distantPast
                                }
                }
 
                private func limitFaild(){
                                DispatchQueue.main.async {
                                                let alertController = UIAlertController(title: nil,message: "请在iphone的“设置-隐私-相机”选项中,允许应用访问你的相机", preferredStyle: .alert)
                                                let cancelAction = UIAlertAction(title: "取消", style: .cancel, handler: { action in
                                                                if self.qmui_isPresented(){
                                                                                self.dismiss(animated: true)
                                                                }else{
                                                                                self.navigationController?.popViewController(animated: true)
                                                                }
                                                })
 
                                                let okAction = UIAlertAction(title: "前往", style: .default, handler: {action in
 
                                                                if self.qmui_isPresented(){
                                                                                self.dismiss(animated: true)
                                                                }else{
                                                                                self.navigationController?.popViewController(animated: true)
                                                                }
 
 
                                                                let settingUrl = NSURL(string: UIApplication.openSettingsURLString)!
                                                                if UIApplication.shared.canOpenURL(settingUrl as URL){
                                                                                if #available(iOS 10.0, *) {
                                                                                                UIApplication.shared.open(settingUrl as URL, options: [:], completionHandler: nil)
                                                                                } else {
                                                                                                UIApplication.shared.openURL(settingUrl as URL)
                                                                                }
                                                                }
                                                })
 
                                                alertController.addAction(cancelAction)
                                                alertController.addAction(okAction)
                                                self.present(alertController, animated: true, completion: nil)
                                }
                }
 
                func checkCameraAuth() -> Bool {
                                let status = AVCaptureDevice.authorizationStatus(for: .video)
                                return  status == .authorized;
                }
 
                //MARK:生成背景遮盖
                func getCoverView(left: CGFloat, top: CGFloat) -> UIImage? {
                                UIGraphicsBeginImageContext(CGSize(width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height - 64))
                                let contextRef = UIGraphicsGetCurrentContext()
                                contextRef?.setFillColor(UIColor (red: 111/255.0, green: 111/255.0, blue: 22/255.0, alpha: 0.5).cgColor)
                                contextRef?.fill(CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height))
                                let inset:CGFloat =   4
                                contextRef?.clear(CGRect(x: left + inset, y: top + inset, width: UIScreen.main.bounds.size.width - left * 2 - inset * 2, height: UIScreen.main.bounds.size.width - left * 2 - inset * 2))
                                return UIGraphicsGetImageFromCurrentImageContext()
                }
 
                func addSubviews() {
                                let _: CGFloat = (UIScreen.main.bounds.size.width - borderWidth) * 0.5
 
                                //扫描边框
                                borderView!.frame = CGRect.init(x: (UIScreen.main.bounds.size.width-(self.view.center.x+30))/2, y: (UIScreen.main.bounds.size.height-(self.view.center.x + 30))/2, width: self.view.center.x + 30, height: self.view.center.x + 30)
                                view.addSubview(borderView!)
 
                                //扫描的线
                                scanLineView!.frame = CGRect(x:borderView!.frame.origin.x+2, y:   borderView!.frame.origin.y+2, width: borderView!.bounds.size.width, height: 2)
                                view.addSubview(scanLineView!)
 
 
                                let tipLabel = UILabel();
                                tipLabel.text = "对准二维码,即可自动扫描";
                                tipLabel.textColor = UIColor.white;
                                tipLabel.font = UIFont.systemFont(ofSize: 14)
                                tipLabel.textAlignment = .center;
                                tipLabel.frame = CGRect(x: 0, y:0, width:200, height: 40)
                                tipLabel.center = CGPoint(x: borderView!.center.x , y: borderView!.center.y + (self.view.center.x + 30)/2 + 30);
                                view.addSubview(tipLabel)
                }
 
                //MARK:- 开始扫描动画
                @objc func startAnimation() -> Void {
 
                                //让约束从顶部开始
                                var frame = self.scanLineView!.frame
                                frame.origin.y = borderView!.frame.origin.y
                                self.scanLineView!.frame = frame
                                self.scanLineView!.layoutIfNeeded()
 
                                UIView.animate(withDuration: 3.0, animations: {
                                                //改变frame
                                                frame.origin.y = self.borderView!.frame.origin.y+self.borderView!.bounds.size.height
                                                self.scanLineView!.frame = frame
                                                //强制更新界面
                                                self.scanLineView!.layoutIfNeeded()
                                })
                }
 
                func stopAnimation() -> Void {
                                //让约束从顶部开始
                                var frame = self.scanLineView!.frame
                                frame.origin.y = 64 + topMargin
                                self.scanLineView!.frame = frame
                                self.scanLineView!.layoutIfNeeded()
                                scanTimer?.invalidate()
                                scanTimer = nil
                }
 
                public override func viewWillDisappear(_ animated: Bool) {
                                super.viewWillDisappear(animated)
 
                                //关闭session
                                if let session = self.session{
                                                session.stopRunning()
                                }
                }
 
                @objc func closeView(){
                                if (self.presentationController != nil) {
                                                self.dismiss(animated: true, completion: nil)
                                }else{
                                                self.navigationController?.popViewController(animated: true)
                                }
                }
 
                deinit {
                                scanTimer?.invalidate()
                                scanTimer = nil
                                print("scan-----deinit")
 
                }
 
                //MARK:设置session
                func setupCaptureSession() {
                                self.session = AVCaptureSession()
 
                                //高质量采集率
                                self.session?.sessionPreset = AVCaptureSession.Preset.high
                                guard let device = AVCaptureDevice.default(for: AVMediaType.video) else { return }
                                //更改这个设置的时候必须先锁定设备,修改完后再解锁,否则崩溃
                                do{
                                                try device.lockForConfiguration()
                                                //设置闪光灯为自动
                                                device.flashMode = AVCaptureDevice.FlashMode.auto
                                                device.unlockForConfiguration()
                                }catch{
                                                print("崩溃")
                                }
                                do{
                                                try self.videoInput = AVCaptureDeviceInput(device: device)
                                }catch{
                                                print("崩溃")
                                }
 
                                //输入设备
                                if let videoInput = self.videoInput {
                                                if self.session?.canAddInput(videoInput) == true {
                                                                self.session?.addInput(videoInput)
                                                }
                                }
 
                                //添加输出
                                if self.session?.canAddOutput(self.metadataOutput) == true{
                                                self.session?.addOutput(self.metadataOutput)
                                }
 
                                self.metadataOutput.metadataObjectTypes = [AVMetadataObject.ObjectType.qr,
                                                                                                                                                                                                            AVMetadataObject.ObjectType.ean13,
                                                                                                                                                                                                            AVMetadataObject.ObjectType.ean8,
                                                                                                                                                                                                            AVMetadataObject.ObjectType.code128,
                                                                                                                                                                                                            AVMetadataObject.ObjectType.code39,
                                                                                                                                                                                                            AVMetadataObject.ObjectType.code93]
 
                                self.metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
 
                                //预览图层
                                if let session = self.session {
                                                self.previewLayer = AVCaptureVideoPreviewLayer(session: session)
                                                self.previewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
                                                self.previewLayer?.frame = CGRect(x: 0, y : 0, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height)
                                }
 
                                if let layer = self.previewLayer {
                                                view.layer.insertSublayer(layer, at: 0)
                                }
                }
 
                public func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
                                self.session?.stopRunning();
                                if (metadataObjects.count >= 1) {
                                                //数组中包含的都是AVMetadataMachineReadableCodeObject 类型的对象,该对象中包含解码后的数据
                                                let qrObject:AVMetadataMachineReadableCodeObject = metadataObjects.last as! AVMetadataMachineReadableCodeObject;
                                                //拿到扫描内容在这里进行个性化处理
                                                let result = qrObject.stringValue
                                                clouse?(result ?? "",firstResult)
                                                closeView()
                                }
                }
 
}