iOS Swift 二维码的生产与扫描

"迈入Swift的学习中"

Posted by 李勇 on 2017-10-25

##一、生成二维码

#1.步骤分析
首先创建一个二维码滤镜实例CIFilter,name为“CIQRCodeGenerator”,实例的设置为默认,将准备生产二维码的内容转为utf8格式的data,为滤镜添加数据key为“inputMessage”,利用滤镜输出二维码,调整二维码的大小即清晰度不然得到的二维码很模糊,整个过程注意可选值的判断。
在二维码中间添加图片时首先开启图片上下文,UIGraphicsBeginImageContext(UISize),先将二维码图片画入上下文,然后将头像画入上下文,通过 UIGraphicsGetImageFromCurrentImageContext()获取新的图片,结束上下文

#2.生产二维码

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
func createQrcode() -> UIImage? {
//1.创建一个二维码滤镜实例(CIFilter)
let filter = CIFilter.init(name: "CIQRCodeGenerator")
// 滤镜恢复默认设置
filter?.setDefaults()
//2.给滤镜添加数据
guard let data = self.urlStr.data(using: String.Encoding.utf8) else {
return nil
}
filter?.setValue(data, forKey: "inputMessage")
//3.生成二维码
guard let ciImg = filter?.outputImage else {
return nil
}
//4.调整清晰度
//创建Transform
let scale = kScreenW / ciImg.extent.width
let transform = CGAffineTransform.init(scaleX: scale, y: scale)
//放大图片
let bigImg = ciImg.applying(transform)
return UIImage.init(ciImage: bigImg)
}

image

#3.在二维码中间添加图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func createQrcodeWithImage() -> UIImage?{
let qrImg = self.createQrcode()
if self.icon != nil && qrImg != nil{
//开启上下文
UIGraphicsBeginImageContext(qrImg!.size)
//把二维码画到上下文
qrImg!.draw(in: CGRect.init(origin: CGPoint.zero, size: qrImg!.size))
//把前景图画到二维码上
let w :CGFloat = 80
self.icon!.draw(in: CGRect.init(x: (qrImg!.size.width - w) * 0.5, y: (qrImg!.size.height - w) * 0.5, width: w, height: w))
//获取新图片
let newImg = UIGraphicsGetImageFromCurrentImageContext()
//关闭上下文
UIGraphicsEndImageContext()
return newImg
}
return qrImg
}

image

##二、扫码

#1.步骤分析
首先设置捕捉设备AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo),获取设备的输入与输出,使用会话实例将输入与输出结合,设置输出的识别类型,新建预览图层加入到当前图层的第一位,设置自动对焦以及扫描区域,设置完毕后开始扫描
实现设备输出的代理方法func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!),播放声音,处理扫描结果

#2.设置扫描设备

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
//设置扫描设备
func setUPScanDevice() {
//设置捕捉设备
do{
//设置设备的输入输出
let input = try AVCaptureDeviceInput(device:device)
let output = AVCaptureMetadataOutput()
output.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
//设置会话
let scanSession = AVCaptureSession()
scanSession.canSetSessionPreset(AVCaptureSessionPresetHigh)
if scanSession.canAddInput(input){
scanSession.addInput(input)
}
if scanSession.canAddOutput(output){
scanSession.addOutput(output)
}
//设置扫描类型(二维码和条形码)
output.metadataObjectTypes = [
AVMetadataObjectTypeQRCode,
AVMetadataObjectTypeCode39Code,
AVMetadataObjectTypeCode128Code,
AVMetadataObjectTypeCode39Mod43Code,
AVMetadataObjectTypeEAN13Code,
AVMetadataObjectTypeEAN8Code,
AVMetadataObjectTypeCode93Code
// AVMetadataObjectTypeFace
]
//预览图层
let scanPreviewLayer = AVCaptureVideoPreviewLayer.init(session: scanSession)
scanPreviewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
scanPreviewLayer?.frame = view.layer.bounds
view.layer.insertSublayer(scanPreviewLayer!, at: 0)
//自动对焦
if (device?.isFocusModeSupported(.autoFocus))!{
do {
try input.device.lockForConfiguration()
}catch{}
input.device.focusMode = .autoFocus
input.device.unlockForConfiguration()
}
//设置扫描区域
// NotificationCenter.default.addObserver(forName: NSNotification.Name.AVCaptureInputPortFormatDescriptionDidChange, object: nil, queue: nil, using: { [weak self] (noti) in
// output.rectOfInterest = (scanPreviewLayer?.metadataOutputRectOfInterest(for: self!.scanPane.frame))!
// })
//保存会话
self.scanSession = scanSession
if !scanSession.isRunning{
scanSession.startRunning()
}
}catch{
//摄像头不可用
LYProgressHUD.showError("相机不可用")
self.navigationController?.popViewController(animated: true)
}
}

#3.处理扫描结果

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
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) {
//停止扫描
self.scanSession.stopRunning()
//建立的SystemSoundID对象
var soundID:SystemSoundID = 0
//获取声音地址
let path = Bundle.main.path(forResource: "scansound", ofType: "wav")
//地址转换
let baseURL = NSURL(fileURLWithPath: path!)
//赋值
AudioServicesCreateSystemSoundID(baseURL, &soundID)
//提醒
AudioServicesPlaySystemSound(soundID)
//扫描结果
if metadataObjects.count > 0{
if let resultObj = metadataObjects.first as? AVMetadataMachineReadableCodeObject{
if self.scanResultBlock != nil{
self.scanResultBlock!(resultObj.stringValue)
self.navigationController?.popViewController(animated: true)
}
}
}
}

#4.其他设备操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//亮灯
do{
try device?.lockForConfiguration()
device?.torchMode = AVCaptureTorchMode.on
device?.flashMode = AVCaptureFlashMode.on
device?.unlockForConfiguration()
}catch{
}
//关灯
do{
try device?.lockForConfiguration()
device?.torchMode = AVCaptureTorchMode.off
device?.flashMode = AVCaptureFlashMode.off
device?.unlockForConfiguration()
}catch{
}

image

##三、整体代码—专为懒人和小白添加

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
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
import UIKit
class CreateQrcodeView: UIView {
var animateFrame : CGRect = CGRect.init(x: 0, y: 0, width: kScreenW, height: kScreenH)
var urlStr = ""
var icon : UIImage?
var shareBlock : (() -> Void)?
init(frame:CGRect?,urlStr:String, image:UIImage?) {
self.urlStr = urlStr
self.icon = image
if frame == nil{
self.animateFrame = CGRect.init(x: 0, y: 0, width: kScreenW, height: kScreenH)
super.init(frame: CGRect.init(x: 0, y: 0, width: kScreenW, height: kScreenH))
}else{
self.animateFrame = frame!
super.init(frame: frame!)
}
self.setUpUI()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setUpUI() {
let bgBtn = UIButton(frame:self.bounds)
bgBtn.backgroundColor = UIColor.RGBSA(s: 0, a: 0.3)
bgBtn.addTarget(self, action: #selector(CreateQrcodeView.hide), for: .touchUpInside)
self.addSubview(bgBtn)
let centerView = UIView()
centerView.x = self.w / 5.0
centerView.w = self.w * 3 / 5.0
centerView.h = centerView.w + 80
centerView.y = (self.h - centerView.h) / 2.0
centerView.backgroundColor = UIColor.white
centerView.clipsToBounds = true
centerView.layer.cornerRadius = 5
self.addSubview(centerView)
let shareBtn = UIButton(frame:CGRect.init(x: centerView.w-50, y: 0, width: 50, height: 50))
shareBtn.setImage(#imageLiteral(resourceName: "Share_code"), for: .normal)
shareBtn.addTarget(self, action: #selector(CreateQrcodeView.share), for: .touchUpInside)
centerView.addSubview(shareBtn)
let codeImgV = UIImageView(frame:CGRect.init(x: 30, y: shareBtn.frame.maxY + 5, width: centerView.w - 60, height: centerView.w-60))
codeImgV.image = self.createQrcodeWithImage()
centerView.addSubview(codeImgV)
let titleLbl = UILabel(frame:CGRect.init(x: 10, y: codeImgV.frame.maxY + 20, width: centerView.w - 20, height: 21))
titleLbl.font = UIFont.systemFont(ofSize: 14.0)
titleLbl.textColor = UIColor.colorHex(hex: "111111")
titleLbl.textAlignment = .center
titleLbl.text = "扫二维码,加入七小服"
centerView.addSubview(titleLbl)
}
func createQrcodeWithImage() -> UIImage?{
let qrImg = self.createQrcode()
if self.icon != nil && qrImg != nil{
//开启上下文
UIGraphicsBeginImageContext(qrImg!.size)
//把二维码画到上下文
qrImg!.draw(in: CGRect.init(origin: CGPoint.zero, size: qrImg!.size))
//把前景图画到二维码上
let w :CGFloat = 80
self.icon!.draw(in: CGRect.init(x: (qrImg!.size.width - w) * 0.5, y: (qrImg!.size.height - w) * 0.5, width: w, height: w))
//获取新图片
let newImg = UIGraphicsGetImageFromCurrentImageContext()
//关闭上下文
UIGraphicsEndImageContext()
return newImg
}
return qrImg
}
func createQrcode() -> UIImage? {
//1.创建一个二维码滤镜实例(CIFilter)
let filter = CIFilter.init(name: "CIQRCodeGenerator")
// 滤镜恢复默认设置
filter?.setDefaults()
//2.给滤镜添加数据
guard let data = self.urlStr.data(using: String.Encoding.utf8) else {
return nil
}
filter?.setValue(data, forKey: "inputMessage")
//3.生成二维码
guard let ciImg = filter?.outputImage else {
return nil
}
//4.调整清晰度
//创建Transform
let scale = kScreenW / ciImg.extent.width
let transform = CGAffineTransform.init(scaleX: scale, y: scale)
//放大图片
let bigImg = ciImg.applying(transform)
return UIImage.init(ciImage: bigImg)
}
func show() {
self.frame = CGRect.zero
UIApplication.shared.keyWindow?.addSubview(self)
UIApplication.shared.keyWindow?.bringSubview(toFront: self)
UIView.animate(withDuration: 0.25, animations: {
self.frame = self.animateFrame
})
}
func hide() {
UIView.animate(withDuration: 0.25, animations: {
self.frame = CGRect.zero
}) { (completion) in
self.removeFromSuperview()
}
}
func share() {
//推荐给好友
if (self.shareBlock != nil){
self.shareBlock!()
}
}
}
import UIKit
class ScanActionViewController: BaseViewController {
var scanResultBlock : ((String) -> Void)?
fileprivate var scanSession = AVCaptureSession()
fileprivate var scanPane = UIView()
fileprivate let line = UIImageView.init(image: #imageLiteral(resourceName: "scan_line"))
fileprivate let device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
override func viewDidLoad() {
super.viewDidLoad()
//1afa29
//设置扫描框周边
self.setUPAroundView()
//设置扫描设备
self.setUPScanDevice()
// //超时提示返回
// DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 6) {
// LYAlertView.show("提示", "扫描超时,是否重新扫描?", "取消", "确定",{
// if !self.scanSession.isRunning{
// self.scanSession.startRunning()
// }
// },{
// self.navigationController?.popViewController(animated: true)
// })
// }
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(true, animated: false)
UIApplication.shared.statusBarStyle = .lightContent
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: false)
UIApplication.shared.statusBarStyle = .default
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//设置扫描框周边
func setUPAroundView() {
self.scanPane = UIView(frame:CGRect.init(x: (kScreenW - 200)/2.0, y: kScreenH/2.0 - 100, width: 200, height: 200))
self.scanPane.backgroundColor = UIColor.clear
self.view.addSubview(self.scanPane)
//扫描框
let imgV = UIImageView(frame:self.scanPane.bounds)
imgV.image = #imageLiteral(resourceName: "scanning_bg")
imgV.contentMode = .scaleAspectFill
self.scanPane.addSubview(imgV)
//扫描线
line.frame = CGRect.init(x: 5, y: 0, width: 190, height: 10)
self.scanPane.addSubview(line)
self.lineRoll()
//周边半透明
let topView = UIView(frame:CGRect.init(x: 0, y: 0, width: kScreenW, height: kScreenH/2.0 - 100))
topView.backgroundColor = UIColor.RGBA(r: 0, g: 0, b: 0, a: 0.3)
self.view.addSubview(topView)
let rightView = UIView(frame:CGRect.init(x: (kScreenW - 200)/2.0 + 200, y: kScreenH/2.0 - 100, width: (kScreenW - 200)/2.0, height: 200))
rightView.backgroundColor = UIColor.RGBA(r: 0, g: 0, b: 0, a: 0.3)
self.view.addSubview(rightView)
let bottomView = UIView(frame:CGRect.init(x: 0, y: kScreenH/2.0 + 100, width: kScreenW, height: kScreenH/2.0 - 100))
bottomView.backgroundColor = UIColor.RGBA(r: 0, g: 0, b: 0, a: 0.3)
self.view.addSubview(bottomView)
let leftView = UIView(frame:CGRect.init(x: 0, y: kScreenH/2.0 - 100, width: (kScreenW - 200)/2.0, height: 200))
leftView.backgroundColor = UIColor.RGBA(r: 0, g: 0, b: 0, a: 0.3)
self.view.addSubview(leftView)
//返回按钮
let backBtn = UIButton(frame:CGRect.init(x: 5, y: 25, width: 55, height: 33))
backBtn.setImage(#imageLiteral(resourceName: "back_white"), for: .normal)
backBtn.addTarget(self, action: #selector(ScanActionViewController.backAction), for: .touchUpInside)
self.view.addSubview(backBtn)
//打开闪光灯按钮
let btn = UIButton(frame:CGRect.init(x: kScreenW/2.0 - 50, y: kScreenH - 150, width: 100, height: 100))
btn.setImage(#imageLiteral(resourceName: "torch_off"), for: .normal)
btn.setImage(#imageLiteral(resourceName: "torch_on"), for: .selected)
btn.addTarget(self, action: #selector(ScanActionViewController.btnAction(btn:)), for: .touchUpInside)
self.view.addSubview(btn)
}
//返回
func backAction() {
self.navigationController?.popViewController(animated: true)
}
//打开闪光灯按钮
func btnAction(btn:UIButton) {
btn.isSelected = !btn.isSelected
if btn.isSelected{
//亮灯
do{
try device?.lockForConfiguration()
device?.torchMode = AVCaptureTorchMode.on
device?.flashMode = AVCaptureFlashMode.on
device?.unlockForConfiguration()
}catch{
}
}else{
//关灯
do{
try device?.lockForConfiguration()
device?.torchMode = AVCaptureTorchMode.off
device?.flashMode = AVCaptureFlashMode.off
device?.unlockForConfiguration()
}catch{
}
}
}
//扫描线滚动
func lineRoll() {
if line.y == 190{
UIView.animate(withDuration: 2.5, animations: {
self.line.y = 0
}) { (completion) in
self.lineRoll()
}
}else{
line.y = 0
UIView.animate(withDuration: 2.5, animations: {
self.line.y = 190
}) { (completion) in
self.lineRoll()
}
}
}
//设置扫描设备
func setUPScanDevice() {
//设置捕捉设备
do{
//设置设备的输入输出
let input = try AVCaptureDeviceInput(device:device)
let output = AVCaptureMetadataOutput()
output.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
//设置会话
let scanSession = AVCaptureSession()
scanSession.canSetSessionPreset(AVCaptureSessionPresetHigh)
if scanSession.canAddInput(input){
scanSession.addInput(input)
}
if scanSession.canAddOutput(output){
scanSession.addOutput(output)
}
//设置扫描类型(二维码和条形码)
output.metadataObjectTypes = [
AVMetadataObjectTypeQRCode,
AVMetadataObjectTypeCode39Code,
AVMetadataObjectTypeCode128Code,
AVMetadataObjectTypeCode39Mod43Code,
AVMetadataObjectTypeEAN13Code,
AVMetadataObjectTypeEAN8Code,
AVMetadataObjectTypeCode93Code
// AVMetadataObjectTypeFace
]
//预览图层
let scanPreviewLayer = AVCaptureVideoPreviewLayer.init(session: scanSession)
scanPreviewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
scanPreviewLayer?.frame = view.layer.bounds
view.layer.insertSublayer(scanPreviewLayer!, at: 0)
//自动对焦
if (device?.isFocusModeSupported(.autoFocus))!{
do {
try input.device.lockForConfiguration()
}catch{}
input.device.focusMode = .autoFocus
input.device.unlockForConfiguration()
}
//设置扫描区域
// NotificationCenter.default.addObserver(forName: NSNotification.Name.AVCaptureInputPortFormatDescriptionDidChange, object: nil, queue: nil, using: { [weak self] (noti) in
// output.rectOfInterest = (scanPreviewLayer?.metadataOutputRectOfInterest(for: self!.scanPane.frame))!
// })
//保存会话
self.scanSession = scanSession
if !scanSession.isRunning{
scanSession.startRunning()
}
}catch{
//摄像头不可用
LYProgressHUD.showError("相机不可用")
self.navigationController?.popViewController(animated: true)
}
}
}
extension ScanActionViewController : AVCaptureMetadataOutputObjectsDelegate{
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) {
//停止扫描
self.scanSession.stopRunning()
//建立的SystemSoundID对象
var soundID:SystemSoundID = 0
//获取声音地址
let path = Bundle.main.path(forResource: "scansound", ofType: "wav")
//地址转换
let baseURL = NSURL(fileURLWithPath: path!)
//赋值
AudioServicesCreateSystemSoundID(baseURL, &soundID)
//提醒
AudioServicesPlaySystemSound(soundID)
//扫描结果
if metadataObjects.count > 0{
if let resultObj = metadataObjects.first as? AVMetadataMachineReadableCodeObject{
if self.scanResultBlock != nil{
self.scanResultBlock!(resultObj.stringValue)
self.navigationController?.popViewController(animated: true)
}
}
}
}
}