diff --git a/Source/LBXPermissions.swift b/Source/LBXPermissions.swift index 46315b3..e0538e3 100644 --- a/Source/LBXPermissions.swift +++ b/Source/LBXPermissions.swift @@ -11,15 +11,18 @@ import AVFoundation import Photos import AssetsLibrary + + class LBXPermissions: NSObject { - // MARK: - ---获取相册权限 - static func authorizePhotoWith(comletion:@escaping (Bool) -> Void) { + //MARK: ----获取相册权限 + static func authorizePhotoWith(comletion:@escaping (Bool)->Void ) + { let granted = PHPhotoLibrary.authorizationStatus() switch granted { case PHAuthorizationStatus.authorized: comletion(true) - case PHAuthorizationStatus.denied, PHAuthorizationStatus.restricted: + case PHAuthorizationStatus.denied,PHAuthorizationStatus.restricted: comletion(false) case PHAuthorizationStatus.notDetermined: PHPhotoLibrary.requestAuthorization({ (status) in @@ -29,41 +32,45 @@ class LBXPermissions: NSObject { }) } } - - // MARK: - --相机权限 - static func authorizeCameraWith(comletion:@escaping (Bool) -> Void ) { - let granted = AVCaptureDevice.authorizationStatus(for: AVMediaType.video) - + + //MARK: ---相机权限 + static func authorizeCameraWith(comletion:@escaping (Bool)->Void ) + { + let granted = AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo); + switch granted { case .authorized: comletion(true) - break + break; case .denied: comletion(false) - break + break; case .restricted: comletion(false) - break + break; case .notDetermined: - AVCaptureDevice.requestAccess(for: AVMediaType.video, completionHandler: { (granted: Bool) in + AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo, completionHandler: { (granted:Bool) in DispatchQueue.main.async { comletion(granted) } }) } } - - // MARK: 跳转到APP系统设置权限界面 - static func jumpToSystemPrivacySetting() { - let appSetting = URL(string: UIApplicationOpenSettingsURLString) - - if appSetting != nil { + + //MARK:跳转到APP系统设置权限界面 + static func jumpToSystemPrivacySetting() + { + let appSetting = URL(string:UIApplicationOpenSettingsURLString) + + if appSetting != nil + { if #available(iOS 10, *) { UIApplication.shared.open(appSetting!, options: [:], completionHandler: nil) - } else { + } + else{ UIApplication.shared.openURL(appSetting!) } } } - + } diff --git a/Source/LBXScanLineAnimation.swift b/Source/LBXScanLineAnimation.swift index e1b2201..f037023 100644 --- a/Source/LBXScanLineAnimation.swift +++ b/Source/LBXScanLineAnimation.swift @@ -12,66 +12,78 @@ class LBXScanLineAnimation: UIImageView { var isAnimationing = false var animationRect: CGRect = CGRect.zero - - func startAnimatingWithRect(animationRect: CGRect, parentView: UIView, image: UIImage?) { + + func startAnimatingWithRect(animationRect: CGRect, parentView: UIView, image: UIImage?) + { self.image = image self.animationRect = animationRect parentView.addSubview(self) - - self.isHidden = false - - isAnimationing = true - - if image != nil { + + self.isHidden = false; + + isAnimationing = true; + + if image != nil + { stepAnimation() } - + } - - @objc func stepAnimation() { + + @objc func stepAnimation() + { if (!isAnimationing) { - return + return; } - - var frame: CGRect = animationRect - - let hImg = self.image!.size.height * animationRect.size.width / self.image!.size.width - - frame.origin.y -= hImg - frame.size.height = hImg - self.frame = frame - self.alpha = 0.0 - + + var frame:CGRect = animationRect; + + let hImg = self.image!.size.height * animationRect.size.width / self.image!.size.width; + + frame.origin.y -= hImg; + frame.size.height = hImg; + self.frame = frame; + self.alpha = 0.0; + UIView.animate(withDuration: 1.4, animations: { () -> Void in - - self.alpha = 1.0 - - var frame = self.animationRect - let hImg = self.image!.size.height * self.animationRect.size.width / self.image!.size.width - - frame.origin.y += (frame.size.height - hImg) - frame.size.height = hImg - - self.frame = frame - - }, completion: { (_: Bool) -> Void in - - self.perform(#selector(LBXScanLineAnimation.stepAnimation), with: nil, afterDelay: 0.3) + + self.alpha = 1.0; + + var frame = self.animationRect; + let hImg = self.image!.size.height * self.animationRect.size.width / self.image!.size.width; + + frame.origin.y += (frame.size.height - hImg); + frame.size.height = hImg; + + self.frame = frame; + + }, completion:{ (value: Bool) -> Void in + + self.perform(#selector(LBXScanLineAnimation.stepAnimation), with: nil, afterDelay: 0.3) }) - + } - - func stopStepAnimating() { - self.isHidden = true - isAnimationing = false + + func stopStepAnimating() + { + self.isHidden = true; + isAnimationing = false; } - - static public func instance() -> LBXScanLineAnimation { + + static public func instance()->LBXScanLineAnimation + { return LBXScanLineAnimation() } - - deinit { + + deinit + { +// print("LBXScanLineAnimation deinit") stopStepAnimating() } } + + + + + diff --git a/Source/LBXScanNetAnimation.swift b/Source/LBXScanNetAnimation.swift index 589a083..b908958 100644 --- a/Source/LBXScanNetAnimation.swift +++ b/Source/LBXScanNetAnimation.swift @@ -11,62 +11,70 @@ import UIKit class LBXScanNetAnimation: UIImageView { var isAnimationing = false - var animationRect: CGRect = CGRect.zero + var animationRect:CGRect = CGRect.zero - static public func instance() -> LBXScanNetAnimation { + static public func instance()->LBXScanNetAnimation + { return LBXScanNetAnimation() } - - func startAnimatingWithRect(animationRect: CGRect, parentView: UIView, image: UIImage?) { + + func startAnimatingWithRect(animationRect:CGRect, parentView:UIView, image:UIImage?) + { self.image = image self.animationRect = animationRect parentView.addSubview(self) - - self.isHidden = false - - isAnimationing = true - - if (image != nil) { - stepAnimation() + + self.isHidden = false; + + isAnimationing = true; + + if (image != nil) + { + stepAnimation() } - } - - @objc func stepAnimation() { + + @objc func stepAnimation() + { if (!isAnimationing) { - return + return; } - var frame = animationRect - let hImg = self.image!.size.height * animationRect.size.width / self.image!.size.width - - frame.origin.y -= hImg - frame.size.height = hImg - self.frame = frame - - self.alpha = 0.0 - + + var frame = animationRect; + + + let hImg = self.image!.size.height * animationRect.size.width / self.image!.size.width; + + frame.origin.y -= hImg; + frame.size.height = hImg; + self.frame = frame; + + self.alpha = 0.0; + UIView.animate(withDuration: 1.2, animations: { () -> Void in - - self.alpha = 1.0 - - var frame = self.animationRect - let hImg = self.image!.size.height * self.animationRect.size.width / self.image!.size.width - - frame.origin.y += (frame.size.height - hImg) - frame.size.height = hImg - - self.frame = frame - - }, completion: { (_: Bool) -> Void in - - self.perform(#selector(LBXScanNetAnimation.stepAnimation), with: nil, afterDelay: 0.3) - + + self.alpha = 1.0; + + var frame = self.animationRect; + let hImg = self.image!.size.height * self.animationRect.size.width / self.image!.size.width; + + frame.origin.y += (frame.size.height - hImg); + frame.size.height = hImg; + + self.frame = frame; + + }, completion:{ (value: Bool) -> Void in + + self.perform(#selector(LBXScanNetAnimation.stepAnimation), with: nil, afterDelay: 0.3) + }) + } - - func stopStepAnimating() { - self.isHidden = true - isAnimationing = false + + func stopStepAnimating() + { + self.isHidden = true; + isAnimationing = false; } } diff --git a/Source/LBXScanView.swift b/Source/LBXScanView.swift index 7a60215..4b905c4 100644 --- a/Source/LBXScanView.swift +++ b/Source/LBXScanView.swift @@ -8,42 +8,45 @@ import UIKit -open class LBXScanView: UIView { +open class LBXScanView: UIView +{ //扫码区域各种参数 - var viewStyle: LBXScanViewStyle = LBXScanViewStyle() - - //扫码区域 - var scanRetangleRect: CGRect = CGRect.zero - + var viewStyle:LBXScanViewStyle = LBXScanViewStyle() + + //扫码区域 + var scanRetangleRect:CGRect = CGRect.zero + //线条扫码动画封装 - var scanLineAnimation: LBXScanLineAnimation? - + var scanLineAnimation:LBXScanLineAnimation? + //网格扫码动画封装 - var scanNetAnimation: LBXScanNetAnimation? - + var scanNetAnimation:LBXScanNetAnimation? + //线条在中间位置,不移动 - var scanLineStill: UIImageView? - + var scanLineStill:UIImageView? + //启动相机时 菊花等待 - var activityView: UIActivityIndicatorView? - + var activityView:UIActivityIndicatorView? + //启动相机中的提示文字 - var labelReadying: UILabel? - + var labelReadying:UILabel? + //记录动画状态 - var isAnimationing: Bool = false - + var isAnimationing:Bool = false + /** - 初始化扫描界面 - - parameter frame: 界面大小,一般为视频显示区域 - - parameter vstyle: 界面效果参数 - - - returns: instancetype - */ - public init(frame: CGRect, vstyle: LBXScanViewStyle ) { + 初始化扫描界面 + - parameter frame: 界面大小,一般为视频显示区域 + - parameter vstyle: 界面效果参数 + + - returns: instancetype + */ + public init(frame:CGRect, vstyle:LBXScanViewStyle ) + { viewStyle = vstyle - - switch (viewStyle.anmiationStyle) { + + switch (viewStyle.anmiationStyle) + { case LBXScanViewAnimationStyle.LineMove: scanLineAnimation = LBXScanLineAnimation.instance() break @@ -54,228 +57,253 @@ open class LBXScanView: UIView { scanLineStill = UIImageView() scanLineStill?.image = viewStyle.animationImage break - + + default: break } - - var frameTmp = frame + + var frameTmp = frame; frameTmp.origin = CGPoint.zero - + super.init(frame: frameTmp) - + backgroundColor = UIColor.clear } - + override init(frame: CGRect) { - - var frameTmp = frame + + var frameTmp = frame; frameTmp.origin = CGPoint.zero - + super.init(frame: frameTmp) - + backgroundColor = UIColor.clear } - - required public init?(coder aDecoder: NSCoder) { + + required public init?(coder aDecoder: NSCoder) + { self.init() - + } - - deinit { - if (scanLineAnimation != nil) { + + deinit + { + if (scanLineAnimation != nil) + { scanLineAnimation!.stopStepAnimating() } - if (scanNetAnimation != nil) { + if (scanNetAnimation != nil) + { scanNetAnimation!.stopStepAnimating() } - - // print("LBXScanView deinit") + + +// print("LBXScanView deinit") } - + + /** - * 开始扫描动画 - */ - func startScanAnimation() { - if isAnimationing { + * 开始扫描动画 + */ + func startScanAnimation() + { + if isAnimationing + { return } - + isAnimationing = true - - let cropRect: CGRect = getScanRectForAnimation() - - switch viewStyle.anmiationStyle { + + let cropRect:CGRect = getScanRectForAnimation() + + switch viewStyle.anmiationStyle + { case LBXScanViewAnimationStyle.LineMove: - - // print(NSStringFromCGRect(cropRect)) - - scanLineAnimation!.startAnimatingWithRect(animationRect: cropRect, parentView: self, image: viewStyle.animationImage ) + +// print(NSStringFromCGRect(cropRect)) + + scanLineAnimation!.startAnimatingWithRect(animationRect: cropRect, parentView: self, image:viewStyle.animationImage ) break case LBXScanViewAnimationStyle.NetGrid: - - scanNetAnimation!.startAnimatingWithRect(animationRect: cropRect, parentView: self, image: viewStyle.animationImage ) + + scanNetAnimation!.startAnimatingWithRect(animationRect: cropRect, parentView: self, image:viewStyle.animationImage ) break case LBXScanViewAnimationStyle.LineStill: - + let stillRect = CGRect(x: cropRect.origin.x+20, y: cropRect.origin.y + cropRect.size.height/2, width: cropRect.size.width-40, - height: 2) + height: 2); self.scanLineStill?.frame = stillRect - + self.addSubview(scanLineStill!) self.scanLineStill?.isHidden = false - + break - + default: break - + } } - + /** * 开始扫描动画 */ - func stopScanAnimation() { + func stopScanAnimation() + { isAnimationing = false - - switch viewStyle.anmiationStyle { + + switch viewStyle.anmiationStyle + { case LBXScanViewAnimationStyle.LineMove: - + scanLineAnimation?.stopStepAnimating() break case LBXScanViewAnimationStyle.NetGrid: - + scanNetAnimation?.stopStepAnimating() break case LBXScanViewAnimationStyle.LineStill: - self.scanLineStill?.isHidden = true - + self.scanLineStill?.isHidden = true + break - + default: break - + } } + + // Only override drawRect: if you perform custom drawing. // An empty implementation adversely affects performance during animation. - override open func draw(_ rect: CGRect) { + override open func draw(_ rect: CGRect) + { // Drawing code drawScanRect() } - - // MARK: - ---- 绘制扫码效果----- - func drawScanRect() { + + //MARK:----- 绘制扫码效果----- + func drawScanRect() + { let XRetangleLeft = viewStyle.xScanRetangleOffset var sizeRetangle = CGSize(width: self.frame.size.width - XRetangleLeft*2.0, height: self.frame.size.width - XRetangleLeft*2.0) - if viewStyle.whRatio != 1.0 { - let w = sizeRetangle.width - var h: CGFloat = w / viewStyle.whRatio - - let hInt: Int = Int(h) + if viewStyle.whRatio != 1.0 + { + let w = sizeRetangle.width; + var h:CGFloat = w / viewStyle.whRatio + + let hInt:Int = Int(h) h = CGFloat(hInt) - + sizeRetangle = CGSize(width: w, height: h) } - + //扫码区域Y轴最小坐标 let YMinRetangle = self.frame.size.height / 2.0 - sizeRetangle.height/2.0 - viewStyle.centerUpOffset let YMaxRetangle = YMinRetangle + sizeRetangle.height let XRetangleRight = self.frame.size.width - XRetangleLeft - - // print("frame:%@",NSStringFromCGRect(self.frame)) - + + +// print("frame:%@",NSStringFromCGRect(self.frame)) + let context = UIGraphicsGetCurrentContext()! - + + //非扫码区域半透明 - //设置非识别区域颜色 + //设置非识别区域颜色 context.setFillColor(viewStyle.color_NotRecoginitonArea.cgColor) - //填充矩形 - //扫码区域上面填充 + //填充矩形 + //扫码区域上面填充 var rect = CGRect(x: 0, y: 0, width: self.frame.size.width, height: YMinRetangle) - context.fill(rect) - - //扫码区域左边填充 + context.fill(rect) + + + //扫码区域左边填充 rect = CGRect(x: 0, y: YMinRetangle, width: XRetangleLeft, height: sizeRetangle.height) - context.fill(rect) - - //扫码区域右边填充 - rect = CGRect(x: XRetangleRight, y: YMinRetangle, width: XRetangleLeft, height: sizeRetangle.height) - context.fill(rect) - - //扫码区域下面填充 - rect = CGRect(x: 0, y: YMaxRetangle, width: self.frame.size.width, height: self.frame.size.height - YMaxRetangle) - context.fill(rect) - //执行绘画 - context.strokePath() - - if viewStyle.isNeedShowRetangle { + context.fill(rect) + + //扫码区域右边填充 + rect = CGRect(x: XRetangleRight, y: YMinRetangle, width: XRetangleLeft,height: sizeRetangle.height) + context.fill(rect) + + //扫码区域下面填充 + rect = CGRect(x: 0, y: YMaxRetangle, width: self.frame.size.width,height: self.frame.size.height - YMaxRetangle) + context.fill(rect) + //执行绘画 + context.strokePath() + + + if viewStyle.isNeedShowRetangle + { //中间画矩形(正方形) context.setStrokeColor(viewStyle.colorRetangleLine.cgColor) - context.setLineWidth(1) - + context.setLineWidth(1); + context.addRect(CGRect(x: XRetangleLeft, y: YMinRetangle, width: sizeRetangle.width, height: sizeRetangle.height)) - + //CGContextMoveToPoint(context, XRetangleLeft, YMinRetangle); //CGContextAddLineToPoint(context, XRetangleLeft+sizeRetangle.width, YMinRetangle); - + context.strokePath() - + } - scanRetangleRect = CGRect(x: XRetangleLeft, y: YMinRetangle, width: sizeRetangle.width, height: sizeRetangle.height) - + scanRetangleRect = CGRect(x: XRetangleLeft, y: YMinRetangle, width: sizeRetangle.width, height: sizeRetangle.height) + + //画矩形框4格外围相框角 - + //相框角的宽度和高度 - let wAngle = viewStyle.photoframeAngleW - let hAngle = viewStyle.photoframeAngleH - + let wAngle = viewStyle.photoframeAngleW; + let hAngle = viewStyle.photoframeAngleH; + //4个角的 线的宽度 let linewidthAngle = viewStyle.photoframeLineW;// 经验参数:6和4 - + //画扫码矩形以及周边半透明黑色坐标参数 - var diffAngle = linewidthAngle/3 + var diffAngle = linewidthAngle/3; diffAngle = linewidthAngle / 2; //框外面4个角,与框有缝隙 diffAngle = linewidthAngle/2; //框4个角 在线上加4个角效果 diffAngle = 0;//与矩形框重合 - - switch viewStyle.photoframeAngleStyle { + + switch viewStyle.photoframeAngleStyle + { case LBXScanViewPhotoframeAngleStyle.Outer: - diffAngle = linewidthAngle/3//框外面4个角,与框紧密联系在一起 - + diffAngle = linewidthAngle/3//框外面4个角,与框紧密联系在一起 + case LBXScanViewPhotoframeAngleStyle.On: - diffAngle = 0 - + diffAngle = 0 + case LBXScanViewPhotoframeAngleStyle.Inner: - diffAngle = -viewStyle.photoframeLineW/2 + diffAngle = -viewStyle.photoframeLineW/2 } - - context.setStrokeColor(viewStyle.colorAngle.cgColor) - context.setFillColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0) - + + context.setStrokeColor(viewStyle.colorAngle.cgColor); + context.setFillColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0); + // Draw them with a 2.0 stroke width so they are a bit more visible. - context.setLineWidth(linewidthAngle) - + context.setLineWidth(linewidthAngle); + + // let leftX = XRetangleLeft - diffAngle let topY = YMinRetangle - diffAngle let rightX = XRetangleRight + diffAngle let bottomY = YMaxRetangle + diffAngle - + //左上角水平线 context.move(to: CGPoint(x: leftX-linewidthAngle/2, y: topY)) context.addLine(to: CGPoint(x: leftX + wAngle, y: topY)) - + //左上角垂直线 context.move(to: CGPoint(x: leftX, y: topY-linewidthAngle/2)) context.addLine(to: CGPoint(x: leftX, y: topY+hAngle)) - + //左下角水平线 context.move(to: CGPoint(x: leftX-linewidthAngle/2, y: bottomY)) context.addLine(to: CGPoint(x: leftX + wAngle, y: bottomY)) - + //左下角垂直线 context.move(to: CGPoint(x: leftX, y: bottomY+linewidthAngle/2)) context.addLine(to: CGPoint(x: leftX, y: bottomY - hAngle)) @@ -283,83 +311,90 @@ open class LBXScanView: UIView { //右上角水平线 context.move(to: CGPoint(x: rightX+linewidthAngle/2, y: topY)) context.addLine(to: CGPoint(x: rightX - wAngle, y: topY)) - + //右上角垂直线 context.move(to: CGPoint(x: rightX, y: topY-linewidthAngle/2)) context.addLine(to: CGPoint(x: rightX, y: topY + hAngle)) - //右下角水平线 +// 右下角水平线 context.move(to: CGPoint(x: rightX+linewidthAngle/2, y: bottomY)) context.addLine(to: CGPoint(x: rightX - wAngle, y: bottomY)) - + //右下角垂直线 context.move(to: CGPoint(x: rightX, y: bottomY+linewidthAngle/2)) context.addLine(to: CGPoint(x: rightX, y: bottomY - hAngle)) - + context.strokePath() } - - func getScanRectForAnimation() -> CGRect { + + func getScanRectForAnimation() -> CGRect + { let XRetangleLeft = viewStyle.xScanRetangleOffset var sizeRetangle = CGSize(width: self.frame.size.width - XRetangleLeft*2, height: self.frame.size.width - XRetangleLeft*2) - - if viewStyle.whRatio != 1 { + + if viewStyle.whRatio != 1 + { let w = sizeRetangle.width var h = w / viewStyle.whRatio - - let hInt: Int = Int(h) + + + let hInt:Int = Int(h) h = CGFloat(hInt) - + sizeRetangle = CGSize(width: w, height: h) } - + //扫码区域Y轴最小坐标 let YMinRetangle = self.frame.size.height / 2.0 - sizeRetangle.height/2.0 - viewStyle.centerUpOffset //扫码区域坐标 let cropRect = CGRect(x: XRetangleLeft, y: YMinRetangle, width: sizeRetangle.width, height: sizeRetangle.height) - - return cropRect + + return cropRect; } //根据矩形区域,获取识别区域 - static func getScanRectWithPreView(preView: UIView, style: LBXScanViewStyle) -> CGRect { - let XRetangleLeft = style.xScanRetangleOffset + static func getScanRectWithPreView(preView:UIView, style:LBXScanViewStyle) -> CGRect + { + let XRetangleLeft = style.xScanRetangleOffset; var sizeRetangle = CGSize(width: preView.frame.size.width - XRetangleLeft*2, height: preView.frame.size.width - XRetangleLeft*2) - - if style.whRatio != 1 { + + if style.whRatio != 1 + { let w = sizeRetangle.width var h = w / style.whRatio - - let hInt: Int = Int(h) + + let hInt:Int = Int(h) h = CGFloat(hInt) - + sizeRetangle = CGSize(width: w, height: h) } - + //扫码区域Y轴最小坐标 let YMinRetangle = preView.frame.size.height / 2.0 - sizeRetangle.height/2.0 - style.centerUpOffset //扫码区域坐标 let cropRect = CGRect(x: XRetangleLeft, y: YMinRetangle, width: sizeRetangle.width, height: sizeRetangle.height) - + + //计算兴趣区域 - var rectOfInterest: CGRect - + var rectOfInterest:CGRect + //ref:http://www.cocoachina.com/ios/20141225/10763.html - let size = preView.bounds.size - let p1 = size.height/size.width - - let p2: CGFloat = 1920.0/1080.0 //使用了1080p的图像输出 + let size = preView.bounds.size; + let p1 = size.height/size.width; + + let p2:CGFloat = 1920.0/1080.0 //使用了1080p的图像输出 if p1 < p2 { - let fixHeight = size.width * 1920.0 / 1080.0 - let fixPadding = (fixHeight - size.height)/2 + let fixHeight = size.width * 1920.0 / 1080.0; + let fixPadding = (fixHeight - size.height)/2; rectOfInterest = CGRect(x: (cropRect.origin.y + fixPadding)/fixHeight, y: cropRect.origin.x/size.width, width: cropRect.size.height/fixHeight, height: cropRect.size.width/size.width) - + + } else { - let fixWidth = size.height * 1080.0 / 1920.0 - let fixPadding = (fixWidth - size.width)/2 + let fixWidth = size.height * 1080.0 / 1920.0; + let fixPadding = (fixWidth - size.width)/2; rectOfInterest = CGRect(x: cropRect.origin.y/size.height, y: (cropRect.origin.x + fixPadding)/fixWidth, width: cropRect.size.height/size.height, @@ -367,41 +402,46 @@ open class LBXScanView: UIView { } return rectOfInterest } - - func getRetangeSize() -> CGSize { + + func getRetangeSize()->CGSize + { let XRetangleLeft = viewStyle.xScanRetangleOffset - + var sizeRetangle = CGSize(width: self.frame.size.width - XRetangleLeft*2, height: self.frame.size.width - XRetangleLeft*2) - - let w = sizeRetangle.width - var h = w / viewStyle.whRatio - - let hInt: Int = Int(h) + + let w = sizeRetangle.width; + var h = w / viewStyle.whRatio; + + + let hInt:Int = Int(h) h = CGFloat(hInt) - - sizeRetangle = CGSize(width: w, height: h) - + + sizeRetangle = CGSize(width: w, height: h) + return sizeRetangle } - - func deviceStartReadying(readyStr: String) { + + func deviceStartReadying(readyStr:String) + { let XRetangleLeft = viewStyle.xScanRetangleOffset - + let sizeRetangle = getRetangeSize() - + //扫码区域Y轴最小坐标 let YMinRetangle = self.frame.size.height / 2.0 - sizeRetangle.height/2.0 - viewStyle.centerUpOffset - + //设备启动状态提示 - if (activityView == nil) { + if (activityView == nil) + { self.activityView = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 30, height: 30)) - + activityView?.center = CGPoint(x: XRetangleLeft + sizeRetangle.width/2 - 50, y: YMinRetangle + sizeRetangle.height/2) activityView?.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.whiteLarge - + addSubview(activityView!) - - let labelReadyRect = CGRect(x: activityView!.frame.origin.x + activityView!.frame.size.width + 10, y: activityView!.frame.origin.y, width: 100, height: 30) + + + let labelReadyRect = CGRect(x: activityView!.frame.origin.x + activityView!.frame.size.width + 10, y: activityView!.frame.origin.y, width: 100, height: 30); //print("%@",NSStringFromCGRect(labelReadyRect)) self.labelReadying = UILabel(frame: labelReadyRect) labelReadying?.text = readyStr @@ -410,21 +450,23 @@ open class LBXScanView: UIView { labelReadying?.font = UIFont.systemFont(ofSize: 18.0) addSubview(labelReadying!) } - - addSubview(labelReadying!) - activityView?.startAnimating() - + + addSubview(labelReadying!) + activityView?.startAnimating() + } - - func deviceStopReadying() { - if activityView != nil { + + func deviceStopReadying() + { + if activityView != nil + { activityView?.stopAnimating() activityView?.removeFromSuperview() labelReadying?.removeFromSuperview() - + activityView = nil labelReadying = nil - + } } diff --git a/Source/LBXScanViewController.swift b/Source/LBXScanViewController.swift index 48ce05e..c663ad0 100644 --- a/Source/LBXScanViewController.swift +++ b/Source/LBXScanViewController.swift @@ -11,206 +11,215 @@ import Foundation import AVFoundation public protocol LBXScanViewControllerDelegate: class { - func scanFinished(scanResult: LBXScanResult, error: String?) + func scanFinished(scanResult: LBXScanResult, error: String?) } -open class LBXScanViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate { - - //返回扫码结果,也可以通过继承本控制器,改写该handleCodeResult方法即可 - open weak var scanResultDelegate: LBXScanViewControllerDelegate? - - open var scanObj: LBXScanWrapper? - - open var scanStyle: LBXScanViewStyle? = LBXScanViewStyle() - - open var qRScanView: LBXScanView? +public protocol QRRectDelegate { + func drawwed() +} +open class LBXScanViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate { + + //返回扫码结果,也可以通过继承本控制器,改写该handleCodeResult方法即可 + open weak var scanResultDelegate: LBXScanViewControllerDelegate? + + open var delegate: QRRectDelegate? + + open var scanObj: LBXScanWrapper? + + open var scanStyle: LBXScanViewStyle? = LBXScanViewStyle() + + open var qRScanView: LBXScanView? + + //启动区域识别功能 - open var isOpenInterestRect = false - + open var isOpenInterestRect = false + //识别码的类型 - public var arrayCodeType: [AVMetadataObject.ObjectType]? - + public var arrayCodeType:[AVMetadataObject.ObjectType]? + //是否需要识别后的当前图像 - public var isNeedCodeImage = false - + public var isNeedCodeImage = false + //相机启动提示文字 - public var readyString: String = "loading" + public var readyString:String! = "loading" override open func viewDidLoad() { super.viewDidLoad() + + // Do any additional setup after loading the view. + + // [self.view addSubview:_qRScanView]; self.view.backgroundColor = UIColor.black self.edgesForExtendedLayout = UIRectEdge(rawValue: 0) - } - - open func setNeedCodeImage(needCodeImg: Bool) { - isNeedCodeImage = needCodeImg + + open func setNeedCodeImage(needCodeImg:Bool) + { + isNeedCodeImage = needCodeImg; } //设置框内识别 - open func setOpenInterestRect(isOpen: Bool) { + open func setOpenInterestRect(isOpen:Bool){ isOpenInterestRect = isOpen } - + override open func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) } - + override open func viewDidAppear(_ animated: Bool) { - + super.viewDidAppear(animated) - + drawScanView() - - LBXPermissions.authorizeCameraWith { (granted) in - if granted { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.3, execute: { - self.startScan() - }) - } else { - self.requireUserConfirmation { - LBXPermissions.jumpToSystemPrivacySetting() - } - } - } + + perform(#selector(LBXScanViewController.startScan), with: nil, afterDelay: 0.3) } - - @objc open func startScan() { - - if (scanObj == nil) { + + @objc open func startScan() + { + + if (scanObj == nil) + { var cropRect = CGRect.zero - if isOpenInterestRect { - cropRect = LBXScanView.getScanRectWithPreView(preView: self.view, style: scanStyle! ) + if isOpenInterestRect + { + cropRect = LBXScanView.getScanRectWithPreView(preView: self.view, style:scanStyle! ) } - + //指定识别几种码 - if arrayCodeType == nil { - arrayCodeType = [AVMetadataObject.ObjectType.qr, AVMetadataObject.ObjectType.ean13, AVMetadataObject.ObjectType.code128] + if arrayCodeType == nil + { + arrayCodeType = [AVMetadataObjectTypeQRCode as NSString ,AVMetadataObjectTypeEAN13Code as NSString ,AVMetadataObjectTypeCode128Code as NSString] } - - scanObj = try? LBXScanWrapper(videoPreView: self.view, objType: arrayCodeType!, isCaptureImg: isNeedCodeImage, cropRect: cropRect, success: { [weak self] (arrayResult) -> Void in - - if let strongSelf = self { + + scanObj = LBXScanWrapper(videoPreView: self.view,objType:arrayCodeType!, isCaptureImg: isNeedCodeImage,cropRect:cropRect, success: { [weak self] (arrayResult) -> Void in + + if let strongSelf = self + { //停止扫描动画 strongSelf.qRScanView?.stopScanAnimation() - + strongSelf.handleCodeResult(arrayResult: arrayResult) } - }) + }) } - + //结束相机等待提示 qRScanView?.deviceStopReadying() - + //开始扫描动画 qRScanView?.startScanAnimation() - + //相机运行 scanObj?.start() } - - open func drawScanView() { - if qRScanView == nil { - qRScanView = LBXScanView(frame: self.view.frame, vstyle: scanStyle! ) + + open func drawScanView() + { + if qRScanView == nil + { + qRScanView = LBXScanView(frame: self.view.frame,vstyle:scanStyle! ) self.view.addSubview(qRScanView!) + delegate?.drawwed() } qRScanView?.deviceStartReadying(readyStr: readyString) - + } - + + /** 处理扫码结果,如果是继承本控制器的,可以重写该方法,作出相应地处理,或者设置delegate作出相应处理 */ - open func handleCodeResult(arrayResult: [LBXScanResult]) { - if let delegate = scanResultDelegate { - + open func handleCodeResult(arrayResult:[LBXScanResult]) + { + if let delegate = scanResultDelegate { + self.navigationController? .popViewController(animated: true) - let result: LBXScanResult = arrayResult[0] - + let result:LBXScanResult = arrayResult[0] + delegate.scanFinished(scanResult: result, error: nil) - } else { - - for result: LBXScanResult in arrayResult { - print("%@", result.strScanned ?? "") + }else{ + + for result:LBXScanResult in arrayResult + { + print("%@",result.strScanned ?? "") } - - let result: LBXScanResult = arrayResult[0] - + + let result:LBXScanResult = arrayResult[0] + showMsg(title: result.strBarCodeType, message: result.strScanned) } } - + override open func viewWillDisappear(_ animated: Bool) { - + NSObject.cancelPreviousPerformRequests(withTarget: self) - + qRScanView?.stopScanAnimation() - + scanObj?.stop() } - - open func openPhotoAlbum() { - LBXPermissions.authorizePhotoWith { [weak self] (_) in - + + open func openPhotoAlbum() + { + LBXPermissions.authorizePhotoWith { [weak self] (granted) in + let picker = UIImagePickerController() - + picker.sourceType = UIImagePickerControllerSourceType.photoLibrary - - picker.delegate = self - + + picker.delegate = self; + picker.allowsEditing = true - - self?.present(picker, animated: true, completion: nil) + + self?.present(picker, animated: true, completion: nil) } } - - // MARK: - ----相册选择图片识别二维码 (条形码没有找到系统方法) - public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String: Any]) { + + //MARK: -----相册选择图片识别二维码 (条形码没有找到系统方法) + public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) + { picker.dismiss(animated: true, completion: nil) - - var image: UIImage? = info[UIImagePickerControllerEditedImage] as? UIImage - - if (image == nil ) { + + var image:UIImage? = info[UIImagePickerControllerEditedImage] as? UIImage + + if (image == nil ) + { image = info[UIImagePickerControllerOriginalImage] as? UIImage } - - if(image != nil) { + + if(image != nil) + { let arrayResult = LBXScanWrapper.recognizeQRImage(image: image!) - if arrayResult.count > 0 { + if arrayResult.count > 0 + { handleCodeResult(arrayResult: arrayResult) return } } - + showMsg(title: nil, message: NSLocalizedString("Identify failed", comment: "Identify failed")) } - - func showMsg(title: String?, message: String?) { - - let alertController = UIAlertController(title: nil, message: message, preferredStyle: UIAlertControllerStyle.alert) - let alertAction = UIAlertAction(title: NSLocalizedString("OK", comment: "OK"), style: UIAlertActionStyle.default) { (_) in - - // if let strongSelf = self - // { - // strongSelf.startScan() - // } - } - - alertController.addAction(alertAction) - present(alertController, animated: true, completion: nil) - } - deinit { - // print("LBXScanViewController deinit") + + func showMsg(title:String?,message:String?) + { + + let alertController = UIAlertController(title: nil, message:message, preferredStyle: UIAlertControllerStyle.alert) + let alertAction = UIAlertAction(title: NSLocalizedString("OK", comment: "OK"), style: UIAlertActionStyle.default) { (alertAction) in + +// if let strongSelf = self +// { +// strongSelf.startScan() +// } + } + + alertController.addAction(alertAction) + present(alertController, animated: true, completion: nil) } - - private func requireUserConfirmation(when confirmed: @escaping () -> Void) { - let alertController = UIAlertController(title: "去开启相机权限", message: nil, preferredStyle: UIAlertControllerStyle.alert) - alertController.addAction(UIAlertAction(title: "好的", style: UIAlertActionStyle.default, handler: { (_) in - confirmed() - })) - alertController.addAction(UIAlertAction(title: "取消", style: UIAlertActionStyle.default, handler: nil)) - present(alertController, animated: true, completion: nil) + deinit + { +// print("LBXScanViewController deinit") } - } diff --git a/Source/LBXScanViewStyle.swift b/Source/LBXScanViewStyle.swift index f79c2fa..54a4077 100644 --- a/Source/LBXScanViewStyle.swift +++ b/Source/LBXScanViewStyle.swift @@ -9,82 +9,89 @@ import UIKit ///扫码区域动画效果 -public enum LBXScanViewAnimationStyle { - case LineMove //线条上下移动 - case NetGrid //网格 - case LineStill //线条停止在扫码区域中央 - case None //无动画 +public enum LBXScanViewAnimationStyle +{ + case LineMove //线条上下移动 + case NetGrid//网格 + case LineStill//线条停止在扫码区域中央 + case None //无动画 } ///扫码区域4个角位置类型 -public enum LBXScanViewPhotoframeAngleStyle { +public enum LBXScanViewPhotoframeAngleStyle +{ case Inner//内嵌,一般不显示矩形框情况下 case Outer//外嵌,包围在矩形框的4个角 case On //在矩形框的4个角上,覆盖 } -public struct LBXScanViewStyle { +public struct LBXScanViewStyle +{ + // MARK: - -中心位置矩形框 - - /// 是否需要绘制扫码矩形框,默认YES - public var isNeedShowRetangle: Bool = true - + + /// 是否需要绘制扫码矩形框,默认YES + public var isNeedShowRetangle:Bool = true + /** - * 默认扫码区域为正方形,如果扫码区域不是正方形,设置宽高比 - */ - public var whRatio: CGFloat = 1.0 - + * 默认扫码区域为正方形,如果扫码区域不是正方形,设置宽高比 + */ + public var whRatio:CGFloat = 1.0 + /** - @brief 矩形框(视频显示透明区)域向上移动偏移量,0表示扫码透明区域在当前视图中心位置,如果负值表示扫码区域下移 - */ - public var centerUpOffset: CGFloat = 44 - + @brief 矩形框(视频显示透明区)域向上移动偏移量,0表示扫码透明区域在当前视图中心位置,如果负值表示扫码区域下移 + */ + public var centerUpOffset:CGFloat = 44 + /** - * 矩形框(视频显示透明区)域离界面左边及右边距离,默认60 - */ - public var xScanRetangleOffset: CGFloat = 60 - + * 矩形框(视频显示透明区)域离界面左边及右边距离,默认60 + */ + public var xScanRetangleOffset:CGFloat = 60 + /** - @brief 矩形框线条颜色,默认白色 - */ + @brief 矩形框线条颜色,默认白色 + */ public var colorRetangleLine = UIColor.white - - // MARK: - 矩形框(扫码区域)周围4个角 - + + + //MARK -矩形框(扫码区域)周围4个角 + /** - @brief 扫码区域的4个角类型 - */ + @brief 扫码区域的4个角类型 + */ public var photoframeAngleStyle = LBXScanViewPhotoframeAngleStyle.Outer - + //4个角的颜色 public var colorAngle = UIColor(red: 0.0, green: 167.0/255.0, blue: 231.0/255.0, alpha: 1.0) - + //扫码区域4个角的宽度和高度 - public var photoframeAngleW: CGFloat = 24.0 - public var photoframeAngleH: CGFloat = 24.0 + public var photoframeAngleW:CGFloat = 24.0 + public var photoframeAngleH:CGFloat = 24.0 /** - @brief 扫码区域4个角的线条宽度,默认6,建议8到4之间 - */ - public var photoframeLineW: CGFloat = 6 - - // MARK: - ---动画效果 - + @brief 扫码区域4个角的线条宽度,默认6,建议8到4之间 + */ + public var photoframeLineW:CGFloat = 6 + + //MARK: ----动画效果 + /** - @brief 扫码动画效果:线条或网格 - */ + @brief 扫码动画效果:线条或网格 + */ public var anmiationStyle = LBXScanViewAnimationStyle.LineMove - + + /** - * 动画效果的图像,如线条或网格的图像 - */ - public var animationImage: UIImage? - - // MARK: - 非识别区域颜色,默认 RGBA (0,0,0,0.5),范围(0--1) - public var color_NotRecoginitonArea: UIColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.5) - - public init() { - + * 动画效果的图像,如线条或网格的图像 + */ + public var animationImage:UIImage? + + + // MARK: -非识别区域颜色,默认 RGBA (0,0,0,0.5),范围(0--1) + public var color_NotRecoginitonArea:UIColor = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.5); + + public init() + { + } - } diff --git a/Source/LBXScanWrapper.swift b/Source/LBXScanWrapper.swift index ef94455..776c0ea 100644 --- a/Source/LBXScanWrapper.swift +++ b/Source/LBXScanWrapper.swift @@ -9,19 +9,20 @@ import UIKit import AVFoundation -public struct LBXScanResult { - +public struct LBXScanResult { + //码内容 - public var strScanned: String? = "" + public var strScanned:String? = "" //扫描图像 - public var imgScanned: UIImage? + public var imgScanned:UIImage? //码的类型 - public var strBarCodeType: String? = "" - + public var strBarCodeType:String? = "" + //码在图像中的位置 - public var arrayCorner: [AnyObject]? - - public init(str: String?, img: UIImage?, barCodeType: String?, corner: [AnyObject]?) { + public var arrayCorner:[AnyObject]? + + public init(str:String?,img:UIImage?,barCodeType:String?,corner:[AnyObject]?) + { self.strScanned = str self.imgScanned = img self.strBarCodeType = barCodeType @@ -29,33 +30,31 @@ public struct LBXScanResult { } } -public enum ScannerError: Error { - case noCaptureDevice -} - -open class LBXScanWrapper: NSObject, AVCaptureMetadataOutputObjectsDelegate { - let device: AVCaptureDevice - - var input: AVCaptureDeviceInput - var output: AVCaptureMetadataOutput +open class LBXScanWrapper: NSObject,AVCaptureMetadataOutputObjectsDelegate { + + let device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo) + + var input:AVCaptureDeviceInput? + var output:AVCaptureMetadataOutput + let session = AVCaptureSession() - var previewLayer: AVCaptureVideoPreviewLayer? - var stillImageOutput: AVCaptureStillImageOutput? - + var previewLayer:AVCaptureVideoPreviewLayer? + var stillImageOutput:AVCaptureStillImageOutput? + //存储返回结果 - var arrayResult: [LBXScanResult] = [] - + var arrayResult:[LBXScanResult] = []; + //扫码结果返回block - var successBlock: ([LBXScanResult]) -> Void - + var successBlock:([LBXScanResult]) -> Void + //是否需要拍照 - var isNeedCaptureImage: Bool - + var isNeedCaptureImage:Bool + //当前扫码结果是否处理 - var isNeedScanResult: Bool = true - + var isNeedScanResult:Bool = true + /** 初始化设备 - parameter videoPreView: 视频显示UIView @@ -65,170 +64,199 @@ open class LBXScanWrapper: NSObject, AVCaptureMetadataOutputObjectsDelegate { - parameter success: 返回识别信息 - returns: */ - init(videoPreView: UIView, objType: [AVMetadataObject.ObjectType] = [.qr], isCaptureImg: Bool, cropRect: CGRect=CGRect.zero, success:@escaping ( ([LBXScanResult]) -> Void) ) - throws { - - guard let device = AVCaptureDevice.default(for: AVMediaType.video) else { - throw ScannerError.noCaptureDevice - } - self.device = device - - do { - input = try AVCaptureDeviceInput(device: device) - } catch let error as NSError { - throw error - } - - successBlock = success - - // Output - output = AVCaptureMetadataOutput() - isNeedCaptureImage = isCaptureImg - stillImageOutput = AVCaptureStillImageOutput() - - super.init() - - if session.canAddInput(input) { - session.addInput(input) - } - if session.canAddOutput(output) { - session.addOutput(output) - } - if session.canAddOutput(stillImageOutput!) { - session.addOutput(stillImageOutput!) - } - - let outputSettings: Dictionary = [AVVideoCodecJPEG: AVVideoCodecKey] - stillImageOutput?.outputSettings = outputSettings - - session.sessionPreset = AVCaptureSession.Preset.high - - //参数设置 - output.setMetadataObjectsDelegate(self, queue: DispatchQueue.main) - - output.metadataObjectTypes = objType - - // output.metadataObjectTypes = [AVMetadataObjectTypeQRCode,AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code] - - if !cropRect.equalTo(CGRect.zero) { - //启动相机后,直接修改该参数无效 - output.rectOfInterest = cropRect + init( videoPreView:UIView,objType:[AVMetadataObject.ObjectType] = [AVMetadataObjectTypeQRCode as NSString],isCaptureImg:Bool,cropRect:CGRect=CGRect.zero,success:@escaping ( ([LBXScanResult]) -> Void) ) + { + do{ + input = try AVCaptureDeviceInput(device: device!) + } + catch let error as NSError { + print("AVCaptureDeviceInput(): \(error)") + } + + successBlock = success + + // Output + output = AVCaptureMetadataOutput() + + isNeedCaptureImage = isCaptureImg + + stillImageOutput = AVCaptureStillImageOutput(); + + super.init() + + if device == nil || input == nil { + return + } + + if session.canAddInput(input!) { + session.addInput(input!) + } + + if session.canAddOutput(output) { + session.addOutput(output) + } + + if session.canAddOutput(stillImageOutput!) { + session.addOutput(stillImageOutput!) + } + + let outputSettings:Dictionary = [AVVideoCodecJPEG:AVVideoCodecKey] + stillImageOutput?.outputSettings = outputSettings + + session.sessionPreset = AVCaptureSessionPresetHigh + + //参数设置 + output.setMetadataObjectsDelegate(self, queue: DispatchQueue.main) + + output.metadataObjectTypes = objType + + // output.metadataObjectTypes = [AVMetadataObjectTypeQRCode,AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code] + + if !cropRect.equalTo(CGRect.zero) + { + //启动相机后,直接修改该参数无效 + output.rectOfInterest = cropRect + } + + previewLayer = AVCaptureVideoPreviewLayer(session: session) + previewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill + + var frame:CGRect = videoPreView.frame + frame.origin = CGPoint.zero + previewLayer?.frame = frame + + videoPreView.layer .insertSublayer(previewLayer!, at: 0) + + if ( device!.isFocusPointOfInterestSupported && device!.isFocusModeSupported(AVCaptureDevice.FocusMode.continuousAutoFocus) ) + { + do + { + try input?.device.lockForConfiguration() + + input?.device.focusMode = AVCaptureDevice.FocusMode.continuousAutoFocus + + input?.device.unlockForConfiguration() } - - previewLayer = AVCaptureVideoPreviewLayer(session: session) - previewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill - - var frame: CGRect = videoPreView.frame - frame.origin = CGPoint.zero - previewLayer?.frame = frame - - videoPreView.layer .insertSublayer(previewLayer!, at: 0) - - if (device.isFocusPointOfInterestSupported && device.isFocusModeSupported(AVCaptureDevice.FocusMode.continuousAutoFocus) ) { - do { - try input.device.lockForConfiguration() - - input.device.focusMode = AVCaptureDevice.FocusMode.continuousAutoFocus - - input.device.unlockForConfiguration() - } catch let error as NSError { - print("device.lockForConfiguration(): \(error)") - - } + catch let error as NSError { + print("device.lockForConfiguration(): \(error)") + } - + } } - + public func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) { captureOutput(output, didOutputMetadataObjects: metadataObjects, from: connection) } - - func start() { - if !session.isRunning { + + func start() + { + if !session.isRunning + { isNeedScanResult = true session.startRunning() } } - func stop() { - if session.isRunning { + func stop() + { + if session.isRunning + { isNeedScanResult = false session.stopRunning() } } - + open func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) { - - if !isNeedScanResult { + + if !isNeedScanResult + { //上一帧处理中 return } - + isNeedScanResult = false - + arrayResult.removeAll() - + //识别扫码类型 - for current: Any in metadataObjects { - if (current as AnyObject).isKind(of: AVMetadataMachineReadableCodeObject.self) { + for current:Any in metadataObjects + { + if (current as AnyObject).isKind(of: AVMetadataMachineReadableCodeObject.self) + { let code = current as! AVMetadataMachineReadableCodeObject - + //码类型 let codeType = code.type // print("code type:%@",codeType) //码内容 let codeContent = code.stringValue // print("code string:%@",codeContent) - + //4个字典,分别 左上角-右上角-右下角-左下角的 坐标百分百,可以使用这个比例抠出码的图像 // let arrayRatio = code.corners - - arrayResult.append(LBXScanResult(str: codeContent, img: UIImage(), barCodeType: codeType.rawValue, corner: code.corners as [AnyObject]?)) + + arrayResult.append(LBXScanResult(str: codeContent, img: UIImage(), barCodeType: codeType, corner: code.corners as [AnyObject]?)) } } - - if arrayResult.count > 0 { - if isNeedCaptureImage { + + if arrayResult.count > 0 + { + if isNeedCaptureImage + { captureImage() - } else { + } + else + { stop() successBlock(arrayResult) } - - } else { + + } + else + { isNeedScanResult = true } - + } - - // MARK: - ---拍照 - open func captureImage() { - let stillImageConnection: AVCaptureConnection? = connectionWithMediaType(mediaType: AVMediaType.video as AVMediaType, connections: (stillImageOutput?.connections)! as [AnyObject]) - - stillImageOutput?.captureStillImageAsynchronously(from: stillImageConnection!, completionHandler: { (imageDataSampleBuffer, _) -> Void in - + + //MARK: ----拍照 + open func captureImage() + { + let stillImageConnection:AVCaptureConnection? = connectionWithMediaType(mediaType: AVMediaTypeVideo as AVMediaType, connections: (stillImageOutput?.connections)! as [AnyObject]) + + + stillImageOutput?.captureStillImageAsynchronously(from: stillImageConnection!, completionHandler: { (imageDataSampleBuffer, error) -> Void in + self.stop() - if imageDataSampleBuffer != nil { + if imageDataSampleBuffer != nil + { let imageData: Data = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(imageDataSampleBuffer!)! - let scanImg: UIImage? = UIImage(data: imageData) - - for idx in 0...self.arrayResult.count-1 { + let scanImg:UIImage? = UIImage(data: imageData) + + + for idx in 0...self.arrayResult.count-1 + { self.arrayResult[idx].imgScanned = scanImg } } - + self.successBlock(self.arrayResult) - + }) } - - open func connectionWithMediaType(mediaType: AVMediaType, connections: [AnyObject]) -> AVCaptureConnection? { - for connection: AnyObject in connections { - let connectionTmp: AVCaptureConnection = connection as! AVCaptureConnection - - for port: Any in connectionTmp.inputPorts { - if (port as AnyObject).isKind(of: AVCaptureInput.Port.self) { - let portTmp: AVCaptureInput.Port = port as! AVCaptureInput.Port - if portTmp.mediaType == (mediaType) { + + open func connectionWithMediaType(mediaType:AVMediaType, connections:[AnyObject]) -> AVCaptureConnection? + { + for connection:AnyObject in connections + { + let connectionTmp:AVCaptureConnection = connection as! AVCaptureConnection + + for port:Any in connectionTmp.inputPorts + { + if (port as AnyObject).isKind(of: AVCaptureInput.Port.self) + { + let portTmp:AVCaptureInput.Port = port as! AVCaptureInput.Port + if portTmp.mediaType == (mediaType as String) + { return connectionTmp } } @@ -236,92 +264,125 @@ open class LBXScanWrapper: NSObject, AVCaptureMetadataOutputObjectsDelegate { } return nil } - - // MARK: 切换识别区域 - open func changeScanRect(cropRect: CGRect) { + + + //MARK:切换识别区域 + open func changeScanRect(cropRect:CGRect) + { //待测试,不知道是否有效 stop() output.rectOfInterest = cropRect start() } - - // MARK: 切换识别码的类型 - open func changeScanType(objType: [AVMetadataObject.ObjectType]) { + + //MARK: 切换识别码的类型 + open func changeScanType(objType:[AVMetadataObject.ObjectType]) + { //待测试中途修改是否有效 output.metadataObjectTypes = objType } - - open func isGetFlash() -> Bool { - if (device.hasFlash && device.hasTorch) { + + open func isGetFlash()->Bool + { + if (device != nil && device!.hasFlash && device!.hasTorch) + { return true } return false } - + /** 打开或关闭闪关灯 - parameter torch: true:打开闪关灯 false:关闭闪光灯 */ - open func setTorch(torch: Bool) { - if isGetFlash() { - do { - try input.device.lockForConfiguration() - - input.device.torchMode = torch ? AVCaptureDevice.TorchMode.on : AVCaptureDevice.TorchMode.off - - input.device.unlockForConfiguration() - } catch let error as NSError { + open func setTorch(torch:Bool) + { + if isGetFlash() + { + do + { + try input?.device.lockForConfiguration() + + input?.device.torchMode = torch ? AVCaptureDevice.TorchMode.on : AVCaptureDevice.TorchMode.off + + input?.device.unlockForConfiguration() + } + catch let error as NSError { print("device.lockForConfiguration(): \(error)") - + } } - + } - + + /** ------闪光灯打开或关闭 */ - open func changeTorch() { - if isGetFlash() { - do { - try input.device.lockForConfiguration() - + open func changeTorch() + { + if isGetFlash() + { + do + { + try input?.device.lockForConfiguration() + var torch = false - - if input.device.torchMode == AVCaptureDevice.TorchMode.on { + + if input?.device.torchMode == AVCaptureDevice.TorchMode.on + { torch = false - } else if input.device.torchMode == AVCaptureDevice.TorchMode.off { + } + else if input?.device.torchMode == AVCaptureDevice.TorchMode.off + { torch = true } - - input.device.torchMode = torch ? AVCaptureDevice.TorchMode.on : AVCaptureDevice.TorchMode.off - - input.device.unlockForConfiguration() - } catch let error as NSError { + + input?.device.torchMode = torch ? AVCaptureDevice.TorchMode.on : AVCaptureDevice.TorchMode.off + + input?.device.unlockForConfiguration() + } + catch let error as NSError { print("device.lockForConfiguration(): \(error)") - + } } } - - // MARK: - -----获取系统默认支持的码的类型 - static func defaultMetaDataObjectTypes() ->[AVMetadataObject.ObjectType] { - var types: [AVMetadataObject.ObjectType] = [.qr, .upce, .code39, .code39Mod43, .ean13, .ean8, .code93, .code128, .pdf417, .aztec] - + + //MARK: ------获取系统默认支持的码的类型 + static func defaultMetaDataObjectTypes() ->[AVMetadataObject.ObjectType] + { + var types = + [AVMetadataObjectTypeQRCode, + AVMetadataObjectTypeUPCECode, + AVMetadataObjectTypeCode39Code, + AVMetadataObjectTypeCode39Mod43Code, + AVMetadataObjectTypeEAN13Code, + AVMetadataObjectTypeEAN8Code, + AVMetadataObjectTypeCode93Code, + AVMetadataObjectTypeCode128Code, + AVMetadataObjectTypePDF417Code, + AVMetadataObjectTypeAztecCode + ] //if #available(iOS 8.0, *) - types += [.interleaved2of5, .itf14, .dataMatrix] - return types + + types.append(AVMetadataObjectTypeInterleaved2of5Code) + types.append(AVMetadataObjectTypeITF14Code) + types.append(AVMetadataObjectTypeDataMatrixCode) + return types as [AVMetadataObject.ObjectType] } - - static func isSysIos8Later() -> Bool { + + + static func isSysIos8Later()->Bool + { // return floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_8_0 - + if #available(iOS 8, *) { - return true + return true; } return false } - + /** 识别二维码码图像 @@ -329,77 +390,91 @@ open class LBXScanWrapper: NSObject, AVCaptureMetadataOutputObjectsDelegate { - returns: 返回识别结果 */ - static open func recognizeQRImage(image: UIImage) -> [LBXScanResult] { - var returnResult: [LBXScanResult]=[] - - if LBXScanWrapper.isSysIos8Later() { + static open func recognizeQRImage(image:UIImage) ->[LBXScanResult] + { + var returnResult:[LBXScanResult]=[] + + if LBXScanWrapper.isSysIos8Later() + { //if #available(iOS 8.0, *) - - let detector: CIDetector = CIDetector(ofType: CIDetectorTypeQRCode, context: nil, options: [CIDetectorAccuracy: CIDetectorAccuracyHigh])! - + + let detector:CIDetector = CIDetector(ofType: CIDetectorTypeQRCode, context: nil, options: [CIDetectorAccuracy:CIDetectorAccuracyHigh])! + let img = CIImage(cgImage: (image.cgImage)!) - - let features: [CIFeature]? = detector.features(in: img, options: [CIDetectorAccuracy: CIDetectorAccuracyHigh]) - - if( features != nil && (features?.count)! > 0) { + + let features:[CIFeature]? = detector.features(in: img, options: [CIDetectorAccuracy:CIDetectorAccuracyHigh]) + + if( features != nil && (features?.count)! > 0) + { let feature = features![0] - - if feature.isKind(of: CIQRCodeFeature.self) { - let featureTmp: CIQRCodeFeature = feature as! CIQRCodeFeature - + + if feature.isKind(of: CIQRCodeFeature.self) + { + let featureTmp:CIQRCodeFeature = feature as! CIQRCodeFeature + let scanResult = featureTmp.messageString - - let result = LBXScanResult(str: scanResult, img: image, barCodeType: AVMetadataObject.ObjectType.qr.rawValue, corner: nil) - + + + let result = LBXScanResult(str: scanResult, img: image, barCodeType: AVMetadataObjectTypeQRCode,corner: nil) + returnResult.append(result) } } - + } - + return returnResult } - - // MARK: - - - 生成二维码,背景色及二维码颜色设置 - static open func createCode( codeType: String, codeString: String, size: CGSize, qrColor: UIColor, bkColor: UIColor ) -> UIImage? { + + + //MARK: -- - 生成二维码,背景色及二维码颜色设置 + static open func createCode( codeType:String, codeString:String, size:CGSize,qrColor:UIColor,bkColor:UIColor )->UIImage? + { //if #available(iOS 8.0, *) - + let stringData = codeString.data(using: String.Encoding.utf8) - + + //系统自带能生成的码 // CIAztecCodeGenerator // CICode128BarcodeGenerator // CIPDF417BarcodeGenerator // CIQRCodeGenerator let qrFilter = CIFilter(name: codeType) - + + qrFilter?.setValue(stringData, forKey: "inputMessage") - + qrFilter?.setValue("H", forKey: "inputCorrectionLevel") - + + //上色 - let colorFilter = CIFilter(name: "CIFalseColor", withInputParameters: ["inputImage": qrFilter!.outputImage!, "inputColor0": CIColor(cgColor: qrColor.cgColor), "inputColor1": CIColor(cgColor: bkColor.cgColor)]) - - let qrImage = colorFilter!.outputImage! - + let colorFilter = CIFilter(name: "CIFalseColor", withInputParameters: ["inputImage":qrFilter!.outputImage!,"inputColor0":CIColor(cgColor: qrColor.cgColor),"inputColor1":CIColor(cgColor: bkColor.cgColor)]) + + + let qrImage = colorFilter!.outputImage!; + //绘制 let cgImage = CIContext().createCGImage(qrImage, from: qrImage.extent)! - - UIGraphicsBeginImageContext(size) - let context = UIGraphicsGetCurrentContext()! - context.interpolationQuality = CGInterpolationQuality.none - context.scaleBy(x: 1.0, y: -1.0) + + + UIGraphicsBeginImageContext(size); + let context = UIGraphicsGetCurrentContext()!; + context.interpolationQuality = CGInterpolationQuality.none; + context.scaleBy(x: 1.0, y: -1.0); context.draw(cgImage, in: context.boundingBoxOfClipPath) - let codeImage = UIGraphicsGetImageFromCurrentImageContext() - UIGraphicsEndImageContext() - + let codeImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return codeImage - + } - - static open func createCode128( codeString: String, size: CGSize, qrColor: UIColor, bkColor: UIColor ) -> UIImage? { + + static open func createCode128( codeString:String, size:CGSize,qrColor:UIColor,bkColor:UIColor )->UIImage? + { let stringData = codeString.data(using: String.Encoding.utf8) - + + //系统自带能生成的码 // CIAztecCodeGenerator 二维码 // CICode128BarcodeGenerator 条形码 @@ -408,93 +483,105 @@ open class LBXScanWrapper: NSObject, AVCaptureMetadataOutputObjectsDelegate { let qrFilter = CIFilter(name: "CICode128BarcodeGenerator") qrFilter?.setDefaults() qrFilter?.setValue(stringData, forKey: "inputMessage") - - let outputImage: CIImage? = qrFilter?.outputImage + + + + let outputImage:CIImage? = qrFilter?.outputImage let context = CIContext() let cgImage = context.createCGImage(outputImage!, from: outputImage!.extent) - + let image = UIImage(cgImage: cgImage!, scale: 1.0, orientation: UIImageOrientation.up) - + + // Resize without interpolating - let scaleRate: CGFloat = 20.0 + let scaleRate:CGFloat = 20.0 let resized = resizeImage(image: image, quality: CGInterpolationQuality.none, rate: scaleRate) - - return resized + + return resized; } - - // MARK: 根据扫描结果,获取图像中得二维码区域图像(如果相机拍摄角度故意很倾斜,获取的图像效果很差) - static func getConcreteCodeImage(srcCodeImage: UIImage, codeResult: LBXScanResult) -> UIImage? { - let rect: CGRect = getConcreteCodeRectFromImage(srcCodeImage: srcCodeImage, codeResult: codeResult) - - if rect.isEmpty { + + + //MARK:根据扫描结果,获取图像中得二维码区域图像(如果相机拍摄角度故意很倾斜,获取的图像效果很差) + static func getConcreteCodeImage(srcCodeImage:UIImage,codeResult:LBXScanResult)->UIImage? + { + let rect:CGRect = getConcreteCodeRectFromImage(srcCodeImage: srcCodeImage, codeResult: codeResult) + + if rect.isEmpty + { return nil } - + let img = imageByCroppingWithStyle(srcImg: srcCodeImage, rect: rect) - - if img != nil { + + if img != nil + { let imgRotation = imageRotation(image: img!, orientation: UIImageOrientation.right) return imgRotation } return nil } //根据二维码的区域截取二维码区域图像 - static open func getConcreteCodeImage(srcCodeImage: UIImage, rect: CGRect) -> UIImage? { - if rect.isEmpty { + static open func getConcreteCodeImage(srcCodeImage:UIImage,rect:CGRect)->UIImage? + { + if rect.isEmpty + { return nil } - + let img = imageByCroppingWithStyle(srcImg: srcCodeImage, rect: rect) - - if img != nil { + + if img != nil + { let imgRotation = imageRotation(image: img!, orientation: UIImageOrientation.right) return imgRotation } return nil } - + //获取二维码的图像区域 - static open func getConcreteCodeRectFromImage(srcCodeImage: UIImage, codeResult: LBXScanResult) -> CGRect { - if (codeResult.arrayCorner == nil || (codeResult.arrayCorner?.count)! < 4 ) { + static open func getConcreteCodeRectFromImage(srcCodeImage:UIImage,codeResult:LBXScanResult)->CGRect + { + if (codeResult.arrayCorner == nil || (codeResult.arrayCorner?.count)! < 4 ) + { return CGRect.zero } - - let corner: [[String: Float]] = codeResult.arrayCorner as! [[String: Float]] - + + let corner:[[String:Float]] = codeResult.arrayCorner as! [[String:Float]] + let dicTopLeft = corner[0] let dicTopRight = corner[1] let dicBottomRight = corner[2] let dicBottomLeft = corner[3] - - let xLeftTopRatio: Float = dicTopLeft["X"]! - let yLeftTopRatio: Float = dicTopLeft["Y"]! - - let xRightTopRatio: Float = dicTopRight["X"]! - let yRightTopRatio: Float = dicTopRight["Y"]! - - let xBottomRightRatio: Float = dicBottomRight["X"]! - let yBottomRightRatio: Float = dicBottomRight["Y"]! - - let xLeftBottomRatio: Float = dicBottomLeft["X"]! - let yLeftBottomRatio: Float = dicBottomLeft["Y"]! - + + let xLeftTopRatio:Float = dicTopLeft["X"]! + let yLeftTopRatio:Float = dicTopLeft["Y"]! + + let xRightTopRatio:Float = dicTopRight["X"]! + let yRightTopRatio:Float = dicTopRight["Y"]! + + let xBottomRightRatio:Float = dicBottomRight["X"]! + let yBottomRightRatio:Float = dicBottomRight["Y"]! + + let xLeftBottomRatio:Float = dicBottomLeft["X"]! + let yLeftBottomRatio:Float = dicBottomLeft["Y"]! + //由于截图只能矩形,所以截图不规则四边形的最大外围 let xMinLeft = CGFloat( min(xLeftTopRatio, xLeftBottomRatio) ) let xMaxRight = CGFloat( max(xRightTopRatio, xBottomRightRatio) ) - + let yMinTop = CGFloat( min(yLeftTopRatio, yRightTopRatio) ) let yMaxBottom = CGFloat ( max(yLeftBottomRatio, yBottomRightRatio) ) - + let imgW = srcCodeImage.size.width let imgH = srcCodeImage.size.height - + //宽高反过来计算 let rect = CGRect(x: xMinLeft * imgH, y: yMinTop*imgW, width: (xMaxRight-xMinLeft)*imgH, height: (yMaxBottom-yMinTop)*imgW) return rect } - - // MARK: - ---图像处理 - + + //MARK: ----图像处理 + /** @brief 图像中间加logo图片 @param srcImg 原图像 @@ -502,99 +589,104 @@ open class LBXScanWrapper: NSObject, AVCaptureMetadataOutputObjectsDelegate { @param logoSize logo图像尺寸 @return 加Logo的图像 */ - static open func addImageLogo(srcImg: UIImage, logoImg: UIImage, logoSize: CGSize ) -> UIImage { - UIGraphicsBeginImageContext(srcImg.size) + static open func addImageLogo(srcImg:UIImage,logoImg:UIImage,logoSize:CGSize )->UIImage + { + UIGraphicsBeginImageContext(srcImg.size); srcImg.draw(in: CGRect(x: 0, y: 0, width: srcImg.size.width, height: srcImg.size.height)) - let rect = CGRect(x: srcImg.size.width/2 - logoSize.width/2, y: srcImg.size.height/2-logoSize.height/2, width: logoSize.width, height: logoSize.height) + let rect = CGRect(x: srcImg.size.width/2 - logoSize.width/2, y: srcImg.size.height/2-logoSize.height/2, width:logoSize.width, height: logoSize.height); logoImg.draw(in: rect) - let resultingImage = UIGraphicsGetImageFromCurrentImageContext() - UIGraphicsEndImageContext() - return resultingImage! + let resultingImage = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return resultingImage!; } - + //图像缩放 - static func resizeImage(image: UIImage, quality: CGInterpolationQuality, rate: CGFloat) -> UIImage? { - var resized: UIImage? - let width = image.size.width * rate - let height = image.size.height * rate - - UIGraphicsBeginImageContext(CGSize(width: width, height: height)) - let context = UIGraphicsGetCurrentContext() - context!.interpolationQuality = quality + static func resizeImage(image:UIImage,quality:CGInterpolationQuality,rate:CGFloat)->UIImage? + { + var resized:UIImage?; + let width = image.size.width * rate; + let height = image.size.height * rate; + + UIGraphicsBeginImageContext(CGSize(width: width, height: height)); + let context = UIGraphicsGetCurrentContext(); + context!.interpolationQuality = quality; image.draw(in: CGRect(x: 0, y: 0, width: width, height: height)) - - resized = UIGraphicsGetImageFromCurrentImageContext() - UIGraphicsEndImageContext() - - return resized + + resized = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return resized; } - + + //图像裁剪 - static func imageByCroppingWithStyle(srcImg: UIImage, rect: CGRect) -> UIImage? { + static func imageByCroppingWithStyle(srcImg:UIImage,rect:CGRect)->UIImage? + { let imageRef = srcImg.cgImage let imagePartRef = imageRef!.cropping(to: rect) let cropImage = UIImage(cgImage: imagePartRef!) - + return cropImage } //图像旋转 - static func imageRotation(image: UIImage, orientation: UIImageOrientation) -> UIImage { - var rotate: Double = 0.0 - var rect: CGRect - var translateX: CGFloat = 0.0 - var translateY: CGFloat = 0.0 - var scaleX: CGFloat = 1.0 - var scaleY: CGFloat = 1.0 - + static func imageRotation(image:UIImage,orientation:UIImageOrientation)->UIImage + { + var rotate:Double = 0.0; + var rect:CGRect; + var translateX:CGFloat = 0.0; + var translateY:CGFloat = 0.0; + var scaleX:CGFloat = 1.0; + var scaleY:CGFloat = 1.0; + switch (orientation) { case UIImageOrientation.left: - rotate = .pi/2 - rect = CGRect(x: 0, y: 0, width: image.size.height, height: image.size.width) - translateX = 0 - translateY = -rect.size.width - scaleY = rect.size.width/rect.size.height - scaleX = rect.size.height/rect.size.width - break + rotate = .pi/2; + rect = CGRect(x: 0, y: 0, width: image.size.height, height: image.size.width); + translateX = 0; + translateY = -rect.size.width; + scaleY = rect.size.width/rect.size.height; + scaleX = rect.size.height/rect.size.width; + break; case UIImageOrientation.right: - rotate = 3 * .pi/2 - rect = CGRect(x: 0, y: 0, width: image.size.height, height: image.size.width) - translateX = -rect.size.height - translateY = 0 - scaleY = rect.size.width/rect.size.height - scaleX = rect.size.height/rect.size.width - break + rotate = 3 * .pi/2; + rect = CGRect(x: 0, y: 0, width: image.size.height, height: image.size.width); + translateX = -rect.size.height; + translateY = 0; + scaleY = rect.size.width/rect.size.height; + scaleX = rect.size.height/rect.size.width; + break; case UIImageOrientation.down: - rotate = .pi - rect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height) - translateX = -rect.size.width - translateY = -rect.size.height - break + rotate = .pi; + rect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height); + translateX = -rect.size.width; + translateY = -rect.size.height; + break; default: - rotate = 0.0 - rect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height) - translateX = 0 - translateY = 0 - break + rotate = 0.0; + rect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height); + translateX = 0; + translateY = 0; + break; } - - UIGraphicsBeginImageContext(rect.size) - let context = UIGraphicsGetCurrentContext()! + + UIGraphicsBeginImageContext(rect.size); + let context = UIGraphicsGetCurrentContext()!; //做CTM变换 - context.translateBy(x: 0.0, y: rect.size.height) - context.scaleBy(x: 1.0, y: -1.0) - context.rotate(by: CGFloat(rotate)) - context.translateBy(x: translateX, y: translateY) - - context.scaleBy(x: scaleX, y: scaleY) + context.translateBy(x: 0.0, y: rect.size.height); + context.scaleBy(x: 1.0, y: -1.0); + context.rotate(by: CGFloat(rotate)); + context.translateBy(x: translateX, y: translateY); + + context.scaleBy(x: scaleX, y: scaleY); //绘制图片 context.draw(image.cgImage!, in: CGRect(x: 0, y: 0, width: rect.size.width, height: rect.size.height)) - let newPic = UIGraphicsGetImageFromCurrentImageContext() - - return newPic! + let newPic = UIGraphicsGetImageFromCurrentImageContext(); + + return newPic!; } - - deinit { - + + deinit + { + // print("LBXScanWrapper deinit") } - }