无故事王国
2023-12-22 c490a22b7cf6949a0b45eb8ee732bace1c023eeb
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
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
//
//  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()
            }
        }
 
 
//        if(!checkCameraAuth()){
//
//            let authStatus = AVCaptureDevice.authorizationStatus(for: AVMediaType.video)
//            if authStatus == AVAuthorizationStatus.notDetermined {
//                AVCaptureDevice.requestAccess(for: AVMediaType.video) {[weak self] (granted) in
//                    if granted{
//                        self?.setupCaptureSession()
//                        self?.addSubviews()
//                        self?.scanTimer = Timer.scheduledTimer(timeInterval: 3.0, target: self, selector: #selector(startAnimation), userInfo: nil, repeats: true)
//                    }
//                }
//            } else if authStatus == AVAuthorizationStatus.restricted || authStatus == AVAuthorizationStatus.denied {
//                let alertController = UIAlertController(title: nil,message: "请在iphone的“设置-隐私-相机”选项中,允许应用访问你的相机", preferredStyle: .alert)
//                let cancelAction = UIAlertAction(title: "取消", style: .cancel, handler: { action in
//                    self.navigationController?.popViewController(animated: true)
//                })
//
//                let okAction = UIAlertAction(title: "前往", style: .default, handler: {action in
//                    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)
//            } else {
//
//            }
//
//        }else{
//            setupCaptureSession()
//            addSubviews()
//            scanTimer = Timer.scheduledTimer(timeInterval: 3.0, target: self, selector: #selector(startAnimation), userInfo: nil, repeats: true)
//        }
    }
 
    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(){
        let alertController = UIAlertController(title: nil,message: "请在iphone的“设置-隐私-相机”选项中,允许应用访问你的相机", preferredStyle: .alert)
        let cancelAction = UIAlertAction(title: "取消", style: .cancel, handler: { action in
            self.navigationController?.popViewController(animated: true)
        })
 
        let okAction = UIAlertAction(title: "前往", style: .default, handler: {action in
            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
 
            //
 
            //        let coverImageView = UIImageView(frame: CGRect(x: 0, y: 64, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height - 64))
            //        coverImageView.backgroundColor = UIColor.clear
            //        coverImageView.image = self.getCoverView(left: leftMargin, top: topMargin)
            //        view.addSubview(coverImageView)
 
 
 
            //扫描边框
        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()
        }
    }
 
}