原文出处:
iOS_小松哥(@iOS_小松哥)
ios7之前我们实现二维码扫描是借助第三方(ZBar
,ZXing
等)来实现的,在ios7之后系统自己提供二维码扫描的方法,性能也要比第三方更好。
今天就来介绍一下原生二维码的使用,包括扫描二维码
,从图片扫描二维码
和生成二维码
。讲解中只展示部分代码,具体请看Github Demo,里面的代码不多,也很容易看懂。
扫描二维码
二维码扫描需要用到AVFoundation.framework
,需要用先创建一个AVCaptureSession,然后设置输入输出流,以及扫描区域和支持的格式:
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
|
//获取摄像设备
AVCaptureDevice *
device
=
[
AVCaptureDevice
defaultDeviceWithMediaType
:
AVMediaTypeVideo
]
;
//创建输入流
AVCaptureDeviceInput *
input
=
[
AVCaptureDeviceInput
deviceInputWithDevice
:
device
error
:
nil
]
;
if
(
!
input
)
{
return
nil
;
}
//创建输出流
AVCaptureMetadataOutput *
output
=
[
[
AVCaptureMetadataOutput
alloc
]
init
]
;
//设置代理 在主线程里刷新
[
output
setMetadataObjectsDelegate
:
self
queue
:
dispatch_get_main_queue
(
)
]
;
//设置扫描区域的比例
CGFloat
width
=
300
/
CGRectGetHeight
(
self
.
view
.
frame
)
;
CGFloat
height
=
300
/
CGRectGetWidth
(
self
.
view
.
frame
)
;
output
.
rectOfInterest
=
CGRectMake
(
(
1
-
width
)
/
2
,
(
1
-
height
)
/
2
,
width
,
height
)
;
AVCaptureSession *
session
=
[
[
AVCaptureSession
alloc
]
init
]
;
//高质量采集率
[
session
setSessionPreset
:
AVCaptureSessionPresetHigh
]
;
[
session
addInput
:
input
]
;
[
session
addOutput
:
output
]
;
//设置扫码支持的编码格式(这里设置条形码和二维码兼容)
output
.
metadataObjectTypes
=
@
[
AVMetadataObjectTypeQRCode
,
AVMetadataObjectTypeEAN13Code
,
AVMetadataObjectTypeEAN8Code
,
AVMetadataObjectTypeCode128Code
]
;
|
然后用这个session
生成一个AVCaptureVideoPreviewLayer
加到某个view的layer上,就可以实时显示摄像头捕捉的内容了:
1
2
3
4
|
AVCaptureVideoPreviewLayer *
layer
=
[
AVCaptureVideoPreviewLayer
layerWithSession
:
self
.
session
]
;
layer
.
videoGravity
=
AVLayerVideoGravityResizeAspectFill
;
layer
.
frame
=
self
.
view
.
layer
.
bounds
;
[
self
.
view
.
layer
insertSublayer
:
layer
atIndex
:
0
]
;
|
然后调用[self.session startRunning];
开始捕获,当扫描出结果后会调用下面的代理方法,其中metadataObject.stringValue
就是扫描后的结果。
1
2
3
4
5
6
7
8
|
#pragma mark - AVCaptureMetadataOutputObjectsDelegate
-
(
void
)
captureOutput
:
(
AVCaptureOutput *
)
captureOutput
didOutputMetadataObjects
:
(
NSArray *
)
metadataObjects
fromConnection
:
(
AVCaptureConnection *
)
connection
{
if
(
metadataObjects
.
count
>
0
)
{
AVMetadataMachineReadableCodeObject *
metadataObject
=
[
metadataObjects
firstObject
]
;
}
}
|
为了在黑夜也可以很好的扫描,可以设置一个闪光灯的开关:
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
|
#pragma mark - 开关闪光灯
-
(
void
)
rightBarButtonDidClick
:
(
UIBarButtonItem *
)
item
{
self
.
flashOpen
=
!
self
.
flashOpen
;
AVCaptureDevice *
device
=
[
AVCaptureDevice
defaultDeviceWithMediaType
:
AVMediaTypeVideo
]
;
if
(
[
device
hasTorch
]
&&
[
device
hasFlash
]
)
{
[
device
lockForConfiguration
:
nil
]
;
if
(
self
.
flashOpen
)
{
device
.
torchMode
=
AVCaptureTorchModeOn
;
device
.
flashMode
=
AVCaptureFlashModeOn
;
}
else
{
device
.
torchMode
=
AVCaptureTorchModeOff
;
device
.
flashMode
=
AVCaptureFlashModeOff
;
}
[
device
unlockForConfiguration
]
;
}
}
|
从图片扫描
有时候我们需要从图片中扫描二维码,或者从相册选择一张图片,代码如下,具体可以看demo。其中feature.messageString
就是扫描后的结果。
1
2
3
4
5
6
7
8
9
10
11
12
|
-
(
void
)
findQRCodeFromImage
:
(
UIImage *
)
image
{
CIDetector *
detector
=
[
CIDetector
detectorOfType
:
CIDetectorTypeQRCode
context
:
nil
options
:
@
{
CIDetectorAccuracy
:
CIDetectorAccuracyHigh
}
]
;
NSArray *
features
=
[
detector
featuresInImage
:
[
CIImage
imageWithCGImage
:
image
.
CGImage
]
]
;
if
(
features
.
count
>=
1
)
{
CIQRCodeFeature *
feature
=
[
features
firstObject
]
;
}
}
|
生成二维码
生成二维码的代码很简单,代码如下。
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
|
/** 生成指定大小的黑白二维码 */
-
(
UIImage *
)
createQRImageWithString
:
(
NSString *
)
string
size
:
(
CGSize
)
size
{
NSData *
stringData
=
[
string
dataUsingEncoding
:
NSUTF8StringEncoding
]
;
CIFilter *
qrFilter
=
[
CIFilter
filterWithName
:
@
"CIQRCodeGenerator"
]
;
// NSLog(@"%@",qrFilter.inputKeys);
[
qrFilter
setValue
:
stringData
forKey
:
@
"inputMessage"
]
;
[
qrFilter
setValue
:
@
"M"
forKey
:
@
"inputCorrectionLevel"
]
;
CIImage *
qrImage
=
qrFilter
.
outputImage
;
//放大并绘制二维码 (上面生成的二维码很小,需要放大)
CGImageRef
cgImage
=
[
[
CIContext
contextWithOptions
:
nil
]
createCGImage
:
qrImage
fromRect
:
qrImage
.
extent
]
;
UIGraphicsBeginImageContext
(
size
)
;
CGContextRef
context
=
UIGraphicsGetCurrentContext
(
)
;
CGContextSetInterpolationQuality
(
context
,
kCGInterpolationNone
)
;
//翻转一下图片 不然生成的QRCode就是上下颠倒的
CGContextScaleCTM
(
context
,
1.0
,
-
1.0
)
;
CGContextDrawImage
(
context
,
CGContextGetClipBoundingBox
(
context
)
,
cgImage
)
;
UIImage *
codeImage
=
UIGraphicsGetImageFromCurrentImageContext
(
)
;
UIGraphicsEndImageContext
(
)
;
CGImageRelease
(
cgImage
)
;
return
codeImage
;
}
|
上面默认生成的时黑白二维码,不过我们也可以改颜色:
1
2
3
4
5
6
7
8
9
10
11
12
|
/** 为二维码改变颜色 */
-
(
UIImage *
)
changeColorForQRImage
:
(
UIImage *
)
image
backColor
:
(
UIColor *
)
backColor
frontColor
:
(
UIColor *
)
frontColor
{
CIFilter *
colorFilter
=
[
CIFilter
filterWithName
:
@
"CIFalseColor"
keysAndValues
:
@
"inputImage"
,
[
CIImage
imageWithCGImage
:
image
.
CGImage
]
,
@
"inputColor0"
,
[
CIColor
colorWithCGColor
:
frontColor
.
CGColor
]
,
@
"inputColor1"
,
[
CIColor
colorWithCGColor
:
backColor
.
CGColor
]
,
nil
]
;
return
[
UIImage
imageWithCIImage
:
colorFilter
.
outputImage
]
;
}
|
有的二维码也会在中心加一个小图片,例如用户头像,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/** 在二维码中心加一个小图 */
-
(
UIImage *
)
addSmallImageForQRImage
:
(
UIImage *
)
qrImage
{
UIGraphicsBeginImageContext
(
qrImage
.
size
)
;
[
qrImage
drawInRect
:
CGRectMake
(
0
,
0
,
qrImage
.
size
.
width
,
qrImage
.
size
.
height
)
]
;
UIImage *
image
=
[
UIImage
imageNamed
:
@
"small"
]
;
CGFloat
imageW
=
50
;
CGFloat
imageX
=
(
qrImage
.
size
.
width
-
imageW
)
*
0.5
;
CGFloat
imgaeY
=
(
qrImage
.
size
.
height
-
imageW
)
*
0.5
;
[
image
drawInRect
:
CGRectMake
(
imageX
,
imgaeY
,
imageW
,
imageW
)
]
;
UIImage *
result
=
UIGraphicsGetImageFromCurrentImageContext
(
)
;
UIGraphicsEndImageContext
(
)
;
return
result
;
}
|
其实也可以扫描条形码,大家可以对着条形码试一试,代码都是通用的。