前言
前两天我搭建了项目的大体架构,今天主要实现的的首页的二维码扫描和二维的生成。废话不多说,直奔主题。
1、二维码扫描
首先来张效果图
界面的搭建我就不废话了,我用storybord搭建,因为懒的去写代码。我主要讲下实现二维码扫描的过程
1.1、扫描线的动画,通过不断改变扫描线于容器视图的约束,来实现的。
@IBOutlet weak var scannerLineConstraint: NSLayoutConstraint! // 扫描线的顶部约束
定义一个定时刷新界面的定时器
var timer:CADisplayLink? // 定义一个定时器
// 设置冲击波的动画
private func startAnimation (){
scannerLineConstraint.constant = -contentViewHeighCons.constant
sannerLineImageView.layoutIfNeeded()
timer = CADisplayLink(target: self ,selector: "updateScanline")
let runloop = NSRunLoop.currentRunLoop()
timer?.addToRunLoop(runloop, forMode: NSRunLoopCommonModes)
timer?.frameInterval = 3
}
// 改变约束的方法
func updateScanline() {
scannerLineConstraint.constant += 10;
if scannerLineConstraint.constant == 0 {
self.scannerLineConstraint.constant = -contentViewHeighCons.constant
}
}
1.2二维码扫描实现,AVFoundation
首先导入AVFoundation
思路:定义一个捕捉会话,然后往会话中添加输入对象(摄像头),添加输出对象,获取扫描结果。
实现:
// 会话
private lazy var captureSession:AVCaptureSession = AVCaptureSession()
// 获取输入设备
private lazy var deviceInput:AVCaptureDeviceInput? = {
//获取摄像头
let device = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
do {
let deviceInput = try AVCaptureDeviceInput(device: device)
return deviceInput
}catch {
print(error)
return nil
}
}()
//创建输出对象
private lazy var output:AVCaptureMetadataOutput = AVCaptureMetadataOutput()
// 创建预览图层
private lazy var previewLayer: AVCaptureVideoPreviewLayer = {
let layer = AVCaptureVideoPreviewLayer(session: self.captureSession)
layer.frame = UIScreen.mainScreen().bounds
return layer
}()
// 创建聚焦的图层
private lazy var focusLayer:CALayer = {
let layer = CALayer()
layer.frame = UIScreen.mainScreen().bounds
return layer
}()
创建好了上面的对象后,首先要判断输入对象和输出对象能否加入到会话中,如果能的才开始扫描。实现代码如下:
//MARK: 开启扫描
private func startScaning() {
// 如果输入和输出不能添加到会话直接返回
if !captureSession.canAddInput(deviceInput) {
return
}
if !captureSession.canAddOutput(output) {
return
}
//分别加输入和输出添加到会话层
captureSession.addInput(deviceInput)
captureSession.addOutput(output)
// 设置输出对象的能够解析的类型
output.metadataObjectTypes = output.availableMetadataObjectTypes
//设置输出代理
output.setMetadataObjectsDelegate(self, queue: dispatch_get_main_queue())
// 添加预览图层
view.layer.insertSublayer(previewLayer, atIndex: 0)
// 开始扫描
captureSession.startRunning()
}
最后就是扫描成功后,数据在输出代理中处理,还有聚焦的实现也是在其中实现。
extension YJQCRScannerViewController:AVCaptureMetadataOutputObjectsDelegate {
internal func captureOutput(captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!) {
print(metadataObjects)
// 1、获取到扫描的数据
let dataString = metadataObjects.last?.stringValue
// 2.获取扫描到的二维码的位置
// 2.1转换坐标
for object in metadataObjects
{
// 2.1.1判断当前获取到的数据, 是否是机器可识别的类型
if object is AVMetadataMachineReadableCodeObject
{
// 2.1.2将坐标转换界面可识别的坐标
let codeObject = previewLayer.transformedMetadataObjectForMetadataObject(object as! AVMetadataObject) as! AVMetadataMachineReadableCodeObject
// 2.1.3绘制图形
drawCorners(codeObject)
}
}
}
/**
绘制图形
:param: codeObject 保存了坐标的对象
*/
private func drawCorners(codeObject: AVMetadataMachineReadableCodeObject)
{
if codeObject.corners.isEmpty
{
return
}
// 1.创建一个图层
let layer = CAShapeLayer()
layer.lineWidth = 4
layer.strokeColor = UIColor.redColor().CGColor
layer.fillColor = UIColor.clearColor().CGColor
// 2.创建路径
let path = UIBezierPath()
var point = CGPointZero
var index: Int = 0
// 2.1移动到第一个点
// 从corners数组中取出第0个元素, 将这个字典中的x/y赋值给point
CGPointMakeWithDictionaryRepresentation((codeObject.corners[index++] as! CFDictionaryRef), &point)
path.moveToPoint(point)
// 2.2移动到其它的点
while index < codeObject.corners.count
{
CGPointMakeWithDictionaryRepresentation((codeObject.corners[index++] as! CFDictionaryRef), &point)
path.addLineToPoint(point)
}
// 2.3关闭路径
path.closePath()
// 2.4绘制路径
layer.path = path.CGPath
// 3.将绘制好的图层添加到drawLayer上
focusLayer.addSublayer(layer)
}
/**
清空边线
*/
private func clearConers(){
// 1.判断drawLayer上是否有其它图层
if focusLayer.sublayers == nil || focusLayer.sublayers?.count == 0{
return
}
// 2.移除所有子图层
for subLayer in focusLayer.sublayers!
{
subLayer.removeFromSuperlayer()
}
}
}
最后就能实现扫描二维码的功能了,还是比较简单的。
2、生成二维的实现
界面如下
二维的生成,主要用的是CoreImage中的CIFilter的滤镜功能。实现过程也是比较简单的。
2.1首先是创建一个滤镜对象
//1.创建滤镜
let filter = CIFilter(name: "CIQRCodeGenerator")
// 设置默认属性
filter?.setDefaults()
2.2 设置需要生成二维的字符串
/设置需要生成二维码的数 filter?.setValue(QRString.dataUsingEncoding(NSUTF8StringEncoding), forKey: "inputMessage")
2.3 获取输出的对象CIImge
//从滤镜中取出生成好的图片
let ciImage = filter?.outputImage
获取的CIImage的二维码,是不清晰,需要处理一下,让其清晰点。我定义一个方法
//将模糊的生成高清的
let bgImage = createNonInterpolatedUIImageFormCIImage(ciImage!, size: 200)
方法具体实现如下
/**
根据CIImage生成指定大小的高清UIImage
:param: image 指定CIImage
:param: size 指定大小
:returns: 生成好的图片
*/
private func createNonInterpolatedUIImageFormCIImage(image: CIImage, size: CGFloat) -> UIImage {
let extent: CGRect = CGRectIntegral(image.extent)
let scale: CGFloat = min(size/CGRectGetWidth(extent), size/CGRectGetHeight(extent))
// 1.创建bitmap;
let width = CGRectGetWidth(extent) * scale
let height = CGRectGetHeight(extent) * scale
let cs: CGColorSpaceRef = CGColorSpaceCreateDeviceGray()!
let bitmapRef = CGBitmapContextCreate(nil, Int(width), Int(height), 8, 0, cs, 0)!
let context = CIContext(options: nil)
let bitmapImage: CGImageRef = context.createCGImage(image, fromRect: extent)
CGContextSetInterpolationQuality(bitmapRef, CGInterpolationQuality.None)
CGContextScaleCTM(bitmapRef, scale, scale);
CGContextDrawImage(bitmapRef, extent, bitmapImage);
// 2.保存bitmap到图片
let scaledImage: CGImageRef = CGBitmapContextCreateImage(bitmapRef)!
return UIImage(CGImage: scaledImage)
}
最后将生成好的二维码,用UIImageView来显示就OK。是不是很简单,到这结束了吧,晚安!有什么错误,望指正,我接触swift的时间也不长!!