123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398 |
- //
- // LBXScanView.swift
- // swiftScan
- //
- // Created by xialibing on 15/12/8.
- // Copyright © 2015年 xialibing. All rights reserved.
- //
- import UIKit
- open class LBXScanView: UIView {
-
- // 扫码区域各种参数
- var viewStyle = LBXScanViewStyle()
- // 扫码区域
- var scanRetangleRect = CGRect.zero
- // 线条扫码动画封装
- var scanLineAnimation: LBXScanLineAnimation?
- // 网格扫码动画封装
- var scanNetAnimation: LBXScanNetAnimation?
-
- // 线条在中间位置,不移动
- var scanLineStill: UIImageView?
- // 启动相机时 菊花等待
- var activityView: UIActivityIndicatorView?
- // 启动相机中的提示文字
- var labelReadying: UILabel?
- // 记录动画状态
- var isAnimationing = false
-
- /**
- 初始化扫描界面
- - parameter frame: 界面大小,一般为视频显示区域
- - parameter vstyle: 界面效果参数
-
- - returns: instancetype
- */
- public init(frame: CGRect, vstyle: LBXScanViewStyle) {
- viewStyle = vstyle
- switch viewStyle.anmiationStyle {
- case LBXScanViewAnimationStyle.LineMove:
- scanLineAnimation = LBXScanLineAnimation.instance()
- case LBXScanViewAnimationStyle.NetGrid:
- scanNetAnimation = LBXScanNetAnimation.instance()
- case LBXScanViewAnimationStyle.LineStill:
- scanLineStill = UIImageView()
- scanLineStill?.image = viewStyle.animationImage
- default:
- break
- }
- var frameTmp = frame
- frameTmp.origin = CGPoint.zero
- super.init(frame: frameTmp)
- backgroundColor = UIColor.clear
- }
-
- override init(frame: CGRect) {
- var frameTmp = frame
- frameTmp.origin = CGPoint.zero
- super.init(frame: frameTmp)
- backgroundColor = UIColor.clear
- }
-
- public required init?(coder aDecoder: NSCoder) {
- self.init()
- }
- deinit {
- if scanLineAnimation != nil {
- scanLineAnimation!.stopStepAnimating()
- }
- if scanNetAnimation != nil {
- scanNetAnimation!.stopStepAnimating()
- }
- }
-
-
- // 开始扫描动画
- func startScanAnimation() {
- guard !isAnimationing else {
- return
- }
- isAnimationing = true
- let cropRect = getScanRectForAnimation()
- switch viewStyle.anmiationStyle {
- case .LineMove:
- scanLineAnimation?.startAnimatingWithRect(animationRect: cropRect,
- parentView: self,
- image: viewStyle.animationImage)
- case .NetGrid:
- scanNetAnimation?.startAnimatingWithRect(animationRect: cropRect,
- parentView: self,
- image: viewStyle.animationImage)
- case .LineStill:
- let stillRect = CGRect(x: cropRect.origin.x + 20,
- y: cropRect.origin.y + cropRect.size.height / 2,
- width: cropRect.size.width - 40,
- height: 2)
- scanLineStill?.frame = stillRect
- addSubview(scanLineStill!)
- scanLineStill?.isHidden = false
- default: break
- }
- }
-
- // 开始扫描动画
- func stopScanAnimation() {
- isAnimationing = false
- switch viewStyle.anmiationStyle {
- case .LineMove:
- scanLineAnimation?.stopStepAnimating()
- case .NetGrid:
- scanNetAnimation?.stopStepAnimating()
- case .LineStill:
- scanLineStill?.isHidden = true
- default: break
- }
- }
-
- // Only override drawRect: if you perform custom drawing.
- // An empty implementation adversely affects performance during animation.
- open override func draw(_ rect: CGRect) {
- drawScanRect()
- }
-
- //MARK: ----- 绘制扫码效果-----
- func drawScanRect() {
- let XRetangleLeft = viewStyle.xScanRetangleOffset
- var sizeRetangle = CGSize(width: frame.size.width - XRetangleLeft * 2.0, height: frame.size.width - XRetangleLeft * 2.0)
-
- if viewStyle.whRatio != 1.0 {
- let w = sizeRetangle.width
- var h = w / viewStyle.whRatio
- h = CGFloat(Int(h))
- sizeRetangle = CGSize(width: w, height: h)
- }
-
- // 扫码区域Y轴最小坐标
- let YMinRetangle = frame.size.height / 2.0 - sizeRetangle.height / 2.0 - viewStyle.centerUpOffset
- let YMaxRetangle = YMinRetangle + sizeRetangle.height
- let XRetangleRight = frame.size.width - XRetangleLeft
-
- guard let context = UIGraphicsGetCurrentContext() else {
- return
- }
-
- // 非扫码区域半透明
- // 设置非识别区域颜色
- context.setFillColor(viewStyle.color_NotRecoginitonArea.cgColor)
- // 填充矩形
- // 扫码区域上面填充
- var rect = CGRect(x: 0, y: 0, width: frame.size.width, height: YMinRetangle)
- 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: frame.size.width, height: frame.size.height - YMaxRetangle)
- context.fill(rect)
- // 执行绘画
- context.strokePath()
-
- if viewStyle.isNeedShowRetangle {
- // 中间画矩形(正方形)
- context.setStrokeColor(viewStyle.colorRetangleLine.cgColor)
- 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)
-
-
- // 画矩形框4格外围相框角
- // 相框角的宽度和高度
- let wAngle = viewStyle.photoframeAngleW
- let hAngle = viewStyle.photoframeAngleH
- // 4个角的 线的宽度
- let linewidthAngle = viewStyle.photoframeLineW // 经验参数:6和4
- // 画扫码矩形以及周边半透明黑色坐标参数
- var diffAngle = linewidthAngle / 3
- diffAngle = linewidthAngle / 2 // 框外面4个角,与框有缝隙
- diffAngle = linewidthAngle / 2 // 框4个角 在线上加4个角效果
- diffAngle = 0 // 与矩形框重合
-
- switch viewStyle.photoframeAngleStyle {
- case .Outer: diffAngle = linewidthAngle / 3 // 框外面4个角,与框紧密联系在一起
- case .On: diffAngle = 0
- case .Inner: diffAngle = -viewStyle.photoframeLineW / 2
- }
-
- 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)
-
-
- //
- 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))
- // 右上角水平线
- 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()
- }
-
- // 根据矩形区域,获取识别区域
- static func getScanRectWithPreView(preView: UIView, style: LBXScanViewStyle) -> CGRect {
- let XRetangleLeft = style.xScanRetangleOffset
- let width = preView.frame.size.width - XRetangleLeft * 2
- let height = width
- var sizeRetangle = CGSize(width: width, height: height)
- if style.whRatio != 1 {
- let w = sizeRetangle.width
- var h = w / style.whRatio
- 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
- // 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的图像输出
- if p1 < p2 {
- 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
- rectOfInterest = CGRect(x: cropRect.origin.y / size.height,
- y: (cropRect.origin.x + fixPadding) / fixWidth,
- width: cropRect.size.height / size.height,
- height: cropRect.size.width / fixWidth)
- }
- return rectOfInterest
- }
-
- func getRetangeSize() -> CGSize {
- let XRetangleLeft = viewStyle.xScanRetangleOffset
- var sizeRetangle = CGSize(width: frame.size.width - XRetangleLeft * 2, height: frame.size.width - XRetangleLeft * 2)
- let w = sizeRetangle.width
- var h = w / viewStyle.whRatio
- h = CGFloat(Int(h))
- sizeRetangle = CGSize(width: w, height: h)
- return sizeRetangle
- }
-
- func deviceStartReadying(readyStr: String) {
- let XRetangleLeft = viewStyle.xScanRetangleOffset
- let sizeRetangle = getRetangeSize()
- // 扫码区域Y轴最小坐标
- let YMinRetangle = frame.size.height / 2.0 - sizeRetangle.height / 2.0 - viewStyle.centerUpOffset
- // 设备启动状态提示
- if activityView == nil {
- 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?.style = UIActivityIndicatorView.Style.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)
- labelReadying = UILabel(frame: labelReadyRect)
- labelReadying?.text = readyStr
- labelReadying?.backgroundColor = UIColor.clear
- labelReadying?.textColor = UIColor.white
- labelReadying?.font = UIFont.systemFont(ofSize: 18.0)
- addSubview(labelReadying!)
- }
- addSubview(labelReadying!)
- activityView?.startAnimating()
- }
-
- func deviceStopReadying() {
- if activityView != nil {
- activityView?.stopAnimating()
- activityView?.removeFromSuperview()
- labelReadying?.removeFromSuperview()
- activityView = nil
- labelReadying = nil
- }
- }
- }
- //MARK: - 公开方法
- public extension LBXScanView {
-
- /// 获取扫描动画的Rect
- func getScanRectForAnimation() -> CGRect {
- let XRetangleLeft = viewStyle.xScanRetangleOffset
- var sizeRetangle = CGSize(width: frame.size.width - XRetangleLeft * 2,
- height: frame.size.width - XRetangleLeft * 2)
-
- if viewStyle.whRatio != 1 {
- let w = sizeRetangle.width
- let h = w / viewStyle.whRatio
- sizeRetangle = CGSize(width: w, height: CGFloat(Int(h)))
- }
-
- // 扫码区域Y轴最小坐标
- let YMinRetangle = 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
- }
-
- }
|