补充部分(扫描界面):
扫面界面的组成部分一般有扫描区域部分,蒙版涂层部分,以及扫面动画部分组成
/*扫描动画部分*/
import UIKit
private let MarginTop:CGFloat = 10
class ScanAnimationView: UIView {
var lineImage:UIImageView!
var timer:Timer!
var addCut:Int!
/*初始化方法*/
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.clear
self.lineImage = UIImageView.init(image: UIImage(named: "homeScanLine"))
self.addSubview(self.lineImage)
lineImage.frame = CGRect(x: 0, y: MarginTop, width: self.bounds.size.width, height: 10)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func draw(_ rect: CGRect) {
self.drawScanView(rect)
}
/*绘制四个小角度*/
func drawScanView(_ rect:CGRect) -> Void {
let width:CGFloat = self.frame.size.width
let height:CGFloat = self.frame.size.height
let lineHeight:CGFloat = width / 15
let lineWidth:CGFloat = width / 15
let ctx:CGContext! = UIGraphicsGetCurrentContext();
ctx.setStrokeColor(UIColor.green.cgColor)
ctx.setLineWidth(3.5)
/*左上角*/
let leftTopPath:CGMutablePath = CGMutablePath.init()
leftTopPath.move(to: CGPoint(x: 0, y: lineHeight))
leftTopPath.addLine(to: CGPoint(x: 0, y: 0))
leftTopPath.addLine(to: CGPoint(x: lineWidth, y: 0))
ctx.addPath(leftTopPath)
ctx.strokePath()
/*右上角*/
let rightTopPath:CGMutablePath = CGMutablePath.init()
rightTopPath.move(to: CGPoint(x: width - lineWidth, y: 0))
rightTopPath.addLine(to: CGPoint(x: width, y: 0))
rightTopPath.addLine(to: CGPoint(x: width, y: lineHeight))
ctx.addPath(rightTopPath)
ctx.strokePath()
/*右下脚*/
let rightBottomPath:CGMutablePath = CGMutablePath.init()
rightBottomPath.move(to: CGPoint(x: width, y: height - lineWidth))
rightBottomPath.addLine(to: CGPoint(x: width, y: height))
rightBottomPath.addLine(to: CGPoint(x: width - lineWidth, y: height))
ctx.addPath(rightBottomPath)
ctx.strokePath()
/*左下角*/
let leftBottomPath:CGMutablePath = CGMutablePath.init()
leftBottomPath.move(to: CGPoint(x: lineWidth, y: height))
leftBottomPath.addLine(to: CGPoint(x: 0, y: height))
leftBottomPath.addLine(to: CGPoint(x: 0, y: height - lineHeight))
ctx.addPath(leftBottomPath)
ctx.strokePath()
}
func startAnimation() -> Void {
if self.timer != nil{
self.timer.invalidate()
}
self.timer = Timer.init(timeInterval: 0.01, repeats: true, block: { (time:Timer) in
if self.lineImage.frame.origin.y > self.frame.size.height - MarginTop {
self.addCut = -1
} else if self.lineImage.frame.origin.y <= MarginTop {
self.addCut = 1
}
self.lineImage.frame = CGRect(x: self.lineImage.frame.origin.x, y: self.lineImage.frame.origin.y + CGFloat(self.addCut), width: self.lineImage.frame.size.width, height: self.lineImage.frame.size.height)
})
RunLoop.main.add(self.timer, forMode: .defaultRunLoopMode)
}
func stopAnimation() -> Void {
self.timer.invalidate()
}
}
/*扫面区域于扫描容器部分*/
// 声明文件
@property (nonatomic,strong) UILabel *label;
- (instancetype)initMaskViewWithFrame:(CGRect)maskFrame
withScanFrame:(CGRect)scanFrame;
@end
// 实现文件
- (instancetype)initMaskViewWithFrame:(CGRect)maskFrame
withScanFrame:(CGRect)scanFrame
{
self = [super initWithFrame:maskFrame];
if (self) {
self.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5];
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
[maskPath appendPath:[[UIBezierPath bezierPathWithRoundedRect:scanFrame cornerRadius:1] bezierPathByReversingPath]];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.path = maskPath.CGPath;
self.layer.mask = maskLayer;
}
return self;
}
二维码的扫描和识别部分:
所使用到的几个类:
1、AVCaptureDevice:
此类用于对摄像头设备的操作,用于获取摄像头设备
2、AVCaptureDeviceInput
此类用于连接摄像头视频捕捉操作
3、AVCaptureMetadataOutput
此类用于摄像头视频流输出操作
4、AVCaptureSession
此类事摄像头提供的对外使用基础硬件的接口类,用于输入输出流于摄像头的绑定操作
5、AVCaptureVideoPreviewLayer
预览视图展示
/*必须类实现(用于摄像头视频捕捉于输入输出)*/
lazy var device:AVCaptureDevice = {
let dev:AVCaptureDevice! = AVCaptureDevice.default(for: AVMediaType.video)
return dev
}()
lazy var input:AVCaptureDeviceInput = {
let inp:AVCaptureDeviceInput = try! AVCaptureDeviceInput.init(device: self.device)
return inp
}()
lazy var outPut:AVCaptureMetadataOutput = {
let out:AVCaptureMetadataOutput = AVCaptureMetadataOutput.init()
out.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
return out
}()
lazy var session:AVCaptureSession = {
let sess:AVCaptureSession = AVCaptureSession.init()
return sess
}()
lazy var displayLayer:AVCaptureVideoPreviewLayer = {
let MarginTop:CGFloat = self.getNavigationBarHeight() + self.getStatusBarHeight()
let layer:AVCaptureVideoPreviewLayer = AVCaptureVideoPreviewLayer.init(session: self.session)
layer.frame = CGRect(x: 0, y: MarginTop, width: self.view.frame.size.width, height: self.view.frame.size.height - MarginTop)
layer.videoGravity = .resizeAspectFill
return layer
}()
/*扫面容器界面于动画界面*/
lazy var scanView:ScanAnimationView = {
let view:ScanAnimationView = ScanAnimationView.init(frame: CGRect(x: self.view.center.x - self.view.frame.size.height/4,
y: self.view.center.y-self.view.frame.size.height/4,
width: self.view.frame.size.height/2,
height: self.view.frame.size.height/2))
return view
}()
lazy var maskView:QRMaskView = {
let MarginTop:CGFloat = self.getNavigationBarHeight() + self.getStatusBarHeight()
self.scanFrame = CGRect(x: self.scanView.frame.origin.x,
y: self.scanView.frame.origin.y - MarginTop,
width: self.scanView.frame.size.width,
height: self.scanView.frame.size.height)
let view:QRMaskView = QRMaskView.init(maskViewWithFrame: CGRect(x: 0, y: MarginTop, width: self.view.frame.size.width, height: self.view.frame.size.height - MarginTop), withScanFrame: scanFrame)
return view
}()
/*扫面动画界面于扫描容器界面加入视图并开启扫描*/
self.view.addSubview(self.scanView)
self.view.addSubview(self.maskView)
self.scanView.startAnimation()
/*开始扫描*/
if self.session.canAddInput(self.input) {
self.session.addInput(self.input)
}
if self.session.canAddOutput(self.outPut) {
self.session.addOutput(self.outPut)
self.outPut.metadataObjectTypes = [.qr,.code39,.code128,.code39Mod43,.ean8,.code93]
}
self.view.layer.insertSublayer(self.displayLayer, at: 0)
self.session.startRunning()
/*扫描结果
扫描结果需要遵循于实现AVCaptureMetadataOutputObjectsDelegate代理方法
*/
/*扫描结果代理方法*/
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
var stringValue:String?
if metadataObjects.count > 0 {
self.session.stopRunning()
if let metadataObject = metadataObjects.first as? AVMetadataMachineReadableCodeObject{
// 扫描结果
stringValue = metadataObject.stringValue
self.navigationController?.popViewController(animated: true)
}
}
}
/*相册二维码识别*/
二维码图片识别采用的是图像识别中的一个简单功能
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 objectAtIndex:0];
NSString *scanResult = feature.messageString;
NSLog(@"%@",scanResult);
}
二维码的生成部分:
此部分我使用的是ZXing第三方进行二维码生成,以解决原生清晰度不符合的问题
func creatMyQRCode() -> Void {
let writer:ZXMultiFormatWriter = ZXMultiFormatWriter.init()
let result:ZXBitMatrix = try! writer.encode("textMyQRCode", format: kBarcodeFormatQRCode, width: Int32(ImageSize), height: Int32(ImageSize))
let zxImage:ZXImage = ZXImage.init(matrix: result)
self.imageView.image = UIImage(cgImage: zxImage.cgimage)
}
二维码生成部分使用原生会出现清晰度问题,有哪位大神有较好的方法可以交流一下