LBXScanView.swift 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. //
  2. // LBXScanView.swift
  3. // swiftScan
  4. //
  5. // Created by xialibing on 15/12/8.
  6. // Copyright © 2015年 xialibing. All rights reserved.
  7. //
  8. import UIKit
  9. open class LBXScanView: UIView
  10. {
  11. //扫码区域各种参数
  12. var viewStyle:LBXScanViewStyle = LBXScanViewStyle()
  13. //扫码区域
  14. var scanRetangleRect:CGRect = CGRect.zero
  15. //线条扫码动画封装
  16. var scanLineAnimation:LBXScanLineAnimation?
  17. //网格扫码动画封装
  18. var scanNetAnimation:LBXScanNetAnimation?
  19. //线条在中间位置,不移动
  20. var scanLineStill:UIImageView?
  21. //启动相机时 菊花等待
  22. var activityView:UIActivityIndicatorView?
  23. //启动相机中的提示文字
  24. var labelReadying:UILabel?
  25. //记录动画状态
  26. var isAnimationing:Bool = false
  27. /**
  28. 初始化扫描界面
  29. - parameter frame: 界面大小,一般为视频显示区域
  30. - parameter vstyle: 界面效果参数
  31. - returns: instancetype
  32. */
  33. public init(frame:CGRect, vstyle:LBXScanViewStyle )
  34. {
  35. viewStyle = vstyle
  36. switch (viewStyle.anmiationStyle)
  37. {
  38. case LBXScanViewAnimationStyle.LineMove:
  39. scanLineAnimation = LBXScanLineAnimation.instance()
  40. break
  41. case LBXScanViewAnimationStyle.NetGrid:
  42. scanNetAnimation = LBXScanNetAnimation.instance()
  43. break
  44. case LBXScanViewAnimationStyle.LineStill:
  45. scanLineStill = UIImageView()
  46. scanLineStill?.image = viewStyle.animationImage
  47. break
  48. default:
  49. break
  50. }
  51. var frameTmp = frame;
  52. frameTmp.origin = CGPoint.zero
  53. super.init(frame: frameTmp)
  54. backgroundColor = UIColor.clear
  55. }
  56. override init(frame: CGRect) {
  57. var frameTmp = frame;
  58. frameTmp.origin = CGPoint.zero
  59. super.init(frame: frameTmp)
  60. backgroundColor = UIColor.clear
  61. }
  62. required public init?(coder aDecoder: NSCoder)
  63. {
  64. self.init()
  65. }
  66. deinit
  67. {
  68. if (scanLineAnimation != nil)
  69. {
  70. scanLineAnimation!.stopStepAnimating()
  71. }
  72. if (scanNetAnimation != nil)
  73. {
  74. scanNetAnimation!.stopStepAnimating()
  75. }
  76. // print("LBXScanView deinit")
  77. }
  78. /**
  79. * 开始扫描动画
  80. */
  81. func startScanAnimation()
  82. {
  83. if isAnimationing
  84. {
  85. return
  86. }
  87. isAnimationing = true
  88. let cropRect:CGRect = getScanRectForAnimation()
  89. switch viewStyle.anmiationStyle
  90. {
  91. case LBXScanViewAnimationStyle.LineMove:
  92. // print(NSStringFromCGRect(cropRect))
  93. scanLineAnimation!.startAnimatingWithRect(animationRect: cropRect, parentView: self, image:viewStyle.animationImage )
  94. break
  95. case LBXScanViewAnimationStyle.NetGrid:
  96. scanNetAnimation!.startAnimatingWithRect(animationRect: cropRect, parentView: self, image:viewStyle.animationImage )
  97. break
  98. case LBXScanViewAnimationStyle.LineStill:
  99. let stillRect = CGRect(x: cropRect.origin.x+20,
  100. y: cropRect.origin.y + cropRect.size.height/2,
  101. width: cropRect.size.width-40,
  102. height: 2);
  103. self.scanLineStill?.frame = stillRect
  104. self.addSubview(scanLineStill!)
  105. self.scanLineStill?.isHidden = false
  106. break
  107. default: break
  108. }
  109. }
  110. /**
  111. * 开始扫描动画
  112. */
  113. func stopScanAnimation()
  114. {
  115. isAnimationing = false
  116. switch viewStyle.anmiationStyle
  117. {
  118. case LBXScanViewAnimationStyle.LineMove:
  119. scanLineAnimation?.stopStepAnimating()
  120. break
  121. case LBXScanViewAnimationStyle.NetGrid:
  122. scanNetAnimation?.stopStepAnimating()
  123. break
  124. case LBXScanViewAnimationStyle.LineStill:
  125. self.scanLineStill?.isHidden = true
  126. break
  127. default: break
  128. }
  129. }
  130. // Only override drawRect: if you perform custom drawing.
  131. // An empty implementation adversely affects performance during animation.
  132. override open func draw(_ rect: CGRect)
  133. {
  134. // Drawing code
  135. drawScanRect()
  136. }
  137. //MARK:----- 绘制扫码效果-----
  138. func drawScanRect()
  139. {
  140. let XRetangleLeft = viewStyle.xScanRetangleOffset
  141. var sizeRetangle = CGSize(width: self.frame.size.width - XRetangleLeft*2.0, height: self.frame.size.width - XRetangleLeft*2.0)
  142. if viewStyle.whRatio != 1.0
  143. {
  144. let w = sizeRetangle.width;
  145. var h:CGFloat = w / viewStyle.whRatio
  146. let hInt:Int = Int(h)
  147. h = CGFloat(hInt)
  148. sizeRetangle = CGSize(width: w, height: h)
  149. }
  150. //扫码区域Y轴最小坐标
  151. let YMinRetangle = self.frame.size.height / 2.0 - sizeRetangle.height/2.0 - viewStyle.centerUpOffset
  152. let YMaxRetangle = YMinRetangle + sizeRetangle.height
  153. let XRetangleRight = self.frame.size.width - XRetangleLeft
  154. // print("frame:%@",NSStringFromCGRect(self.frame))
  155. let context = UIGraphicsGetCurrentContext()!
  156. //非扫码区域半透明
  157. //设置非识别区域颜色
  158. context.setFillColor(viewStyle.color_NotRecoginitonArea.cgColor)
  159. //填充矩形
  160. //扫码区域上面填充
  161. var rect = CGRect(x: 0, y: 0, width: self.frame.size.width, height: YMinRetangle)
  162. context.fill(rect)
  163. //扫码区域左边填充
  164. rect = CGRect(x: 0, y: YMinRetangle, width: XRetangleLeft, height: sizeRetangle.height)
  165. context.fill(rect)
  166. //扫码区域右边填充
  167. rect = CGRect(x: XRetangleRight, y: YMinRetangle, width: XRetangleLeft,height: sizeRetangle.height)
  168. context.fill(rect)
  169. //扫码区域下面填充
  170. rect = CGRect(x: 0, y: YMaxRetangle, width: self.frame.size.width,height: self.frame.size.height - YMaxRetangle)
  171. context.fill(rect)
  172. //执行绘画
  173. context.strokePath()
  174. if viewStyle.isNeedShowRetangle
  175. {
  176. //中间画矩形(正方形)
  177. context.setStrokeColor(viewStyle.colorRetangleLine.cgColor)
  178. context.setLineWidth(1);
  179. context.addRect(CGRect(x: XRetangleLeft, y: YMinRetangle, width: sizeRetangle.width, height: sizeRetangle.height))
  180. //CGContextMoveToPoint(context, XRetangleLeft, YMinRetangle);
  181. //CGContextAddLineToPoint(context, XRetangleLeft+sizeRetangle.width, YMinRetangle);
  182. context.strokePath()
  183. }
  184. scanRetangleRect = CGRect(x: XRetangleLeft, y: YMinRetangle, width: sizeRetangle.width, height: sizeRetangle.height)
  185. //画矩形框4格外围相框角
  186. //相框角的宽度和高度
  187. let wAngle = viewStyle.photoframeAngleW;
  188. let hAngle = viewStyle.photoframeAngleH;
  189. //4个角的 线的宽度
  190. let linewidthAngle = viewStyle.photoframeLineW;// 经验参数:6和4
  191. //画扫码矩形以及周边半透明黑色坐标参数
  192. var diffAngle = linewidthAngle/3;
  193. diffAngle = linewidthAngle / 2; //框外面4个角,与框有缝隙
  194. diffAngle = linewidthAngle/2; //框4个角 在线上加4个角效果
  195. diffAngle = 0;//与矩形框重合
  196. switch viewStyle.photoframeAngleStyle
  197. {
  198. case LBXScanViewPhotoframeAngleStyle.Outer:
  199. diffAngle = linewidthAngle/3//框外面4个角,与框紧密联系在一起
  200. case LBXScanViewPhotoframeAngleStyle.On:
  201. diffAngle = 0
  202. case LBXScanViewPhotoframeAngleStyle.Inner:
  203. diffAngle = -viewStyle.photoframeLineW/2
  204. }
  205. context.setStrokeColor(viewStyle.colorAngle.cgColor);
  206. context.setFillColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0);
  207. // Draw them with a 2.0 stroke width so they are a bit more visible.
  208. context.setLineWidth(linewidthAngle);
  209. //
  210. let leftX = XRetangleLeft - diffAngle
  211. let topY = YMinRetangle - diffAngle
  212. let rightX = XRetangleRight + diffAngle
  213. let bottomY = YMaxRetangle + diffAngle
  214. //左上角水平线
  215. context.move(to: CGPoint(x: leftX-linewidthAngle/2, y: topY))
  216. context.addLine(to: CGPoint(x: leftX + wAngle, y: topY))
  217. //左上角垂直线
  218. context.move(to: CGPoint(x: leftX, y: topY-linewidthAngle/2))
  219. context.addLine(to: CGPoint(x: leftX, y: topY+hAngle))
  220. //左下角水平线
  221. context.move(to: CGPoint(x: leftX-linewidthAngle/2, y: bottomY))
  222. context.addLine(to: CGPoint(x: leftX + wAngle, y: bottomY))
  223. //左下角垂直线
  224. context.move(to: CGPoint(x: leftX, y: bottomY+linewidthAngle/2))
  225. context.addLine(to: CGPoint(x: leftX, y: bottomY - hAngle))
  226. //右上角水平线
  227. context.move(to: CGPoint(x: rightX+linewidthAngle/2, y: topY))
  228. context.addLine(to: CGPoint(x: rightX - wAngle, y: topY))
  229. //右上角垂直线
  230. context.move(to: CGPoint(x: rightX, y: topY-linewidthAngle/2))
  231. context.addLine(to: CGPoint(x: rightX, y: topY + hAngle))
  232. // 右下角水平线
  233. context.move(to: CGPoint(x: rightX+linewidthAngle/2, y: bottomY))
  234. context.addLine(to: CGPoint(x: rightX - wAngle, y: bottomY))
  235. //右下角垂直线
  236. context.move(to: CGPoint(x: rightX, y: bottomY+linewidthAngle/2))
  237. context.addLine(to: CGPoint(x: rightX, y: bottomY - hAngle))
  238. context.strokePath()
  239. }
  240. func getScanRectForAnimation() -> CGRect
  241. {
  242. let XRetangleLeft = viewStyle.xScanRetangleOffset
  243. var sizeRetangle = CGSize(width: self.frame.size.width - XRetangleLeft*2, height: self.frame.size.width - XRetangleLeft*2)
  244. if viewStyle.whRatio != 1
  245. {
  246. let w = sizeRetangle.width
  247. var h = w / viewStyle.whRatio
  248. let hInt:Int = Int(h)
  249. h = CGFloat(hInt)
  250. sizeRetangle = CGSize(width: w, height: h)
  251. }
  252. //扫码区域Y轴最小坐标
  253. let YMinRetangle = self.frame.size.height / 2.0 - sizeRetangle.height/2.0 - viewStyle.centerUpOffset
  254. //扫码区域坐标
  255. let cropRect = CGRect(x: XRetangleLeft, y: YMinRetangle, width: sizeRetangle.width, height: sizeRetangle.height)
  256. return cropRect;
  257. }
  258. //根据矩形区域,获取识别区域
  259. static func getScanRectWithPreView(preView:UIView, style:LBXScanViewStyle) -> CGRect
  260. {
  261. let XRetangleLeft = style.xScanRetangleOffset;
  262. var sizeRetangle = CGSize(width: preView.frame.size.width - XRetangleLeft*2, height: preView.frame.size.width - XRetangleLeft*2)
  263. if style.whRatio != 1
  264. {
  265. let w = sizeRetangle.width
  266. var h = w / style.whRatio
  267. let hInt:Int = Int(h)
  268. h = CGFloat(hInt)
  269. sizeRetangle = CGSize(width: w, height: h)
  270. }
  271. //扫码区域Y轴最小坐标
  272. let YMinRetangle = preView.frame.size.height / 2.0 - sizeRetangle.height/2.0 - style.centerUpOffset
  273. //扫码区域坐标
  274. let cropRect = CGRect(x: XRetangleLeft, y: YMinRetangle, width: sizeRetangle.width, height: sizeRetangle.height)
  275. //计算兴趣区域
  276. var rectOfInterest:CGRect
  277. //ref:http://www.cocoachina.com/ios/20141225/10763.html
  278. let size = preView.bounds.size;
  279. let p1 = size.height/size.width;
  280. let p2:CGFloat = 1920.0/1080.0 //使用了1080p的图像输出
  281. if p1 < p2 {
  282. let fixHeight = size.width * 1920.0 / 1080.0;
  283. let fixPadding = (fixHeight - size.height)/2;
  284. rectOfInterest = CGRect(x: (cropRect.origin.y + fixPadding)/fixHeight,
  285. y: cropRect.origin.x/size.width,
  286. width: cropRect.size.height/fixHeight,
  287. height: cropRect.size.width/size.width)
  288. } else {
  289. let fixWidth = size.height * 1080.0 / 1920.0;
  290. let fixPadding = (fixWidth - size.width)/2;
  291. rectOfInterest = CGRect(x: cropRect.origin.y/size.height,
  292. y: (cropRect.origin.x + fixPadding)/fixWidth,
  293. width: cropRect.size.height/size.height,
  294. height: cropRect.size.width/fixWidth)
  295. }
  296. return rectOfInterest
  297. }
  298. func getRetangeSize()->CGSize
  299. {
  300. let XRetangleLeft = viewStyle.xScanRetangleOffset
  301. var sizeRetangle = CGSize(width: self.frame.size.width - XRetangleLeft*2, height: self.frame.size.width - XRetangleLeft*2)
  302. let w = sizeRetangle.width;
  303. var h = w / viewStyle.whRatio;
  304. let hInt:Int = Int(h)
  305. h = CGFloat(hInt)
  306. sizeRetangle = CGSize(width: w, height: h)
  307. return sizeRetangle
  308. }
  309. func deviceStartReadying(readyStr:String)
  310. {
  311. let XRetangleLeft = viewStyle.xScanRetangleOffset
  312. let sizeRetangle = getRetangeSize()
  313. //扫码区域Y轴最小坐标
  314. let YMinRetangle = self.frame.size.height / 2.0 - sizeRetangle.height/2.0 - viewStyle.centerUpOffset
  315. //设备启动状态提示
  316. if (activityView == nil)
  317. {
  318. self.activityView = UIActivityIndicatorView(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
  319. activityView?.center = CGPoint(x: XRetangleLeft + sizeRetangle.width/2 - 50, y: YMinRetangle + sizeRetangle.height/2)
  320. activityView?.style = UIActivityIndicatorView.Style.whiteLarge
  321. addSubview(activityView!)
  322. let labelReadyRect = CGRect(x: activityView!.frame.origin.x + activityView!.frame.size.width + 10, y: activityView!.frame.origin.y, width: 100, height: 30);
  323. //print("%@",NSStringFromCGRect(labelReadyRect))
  324. self.labelReadying = UILabel(frame: labelReadyRect)
  325. labelReadying?.text = readyStr
  326. labelReadying?.backgroundColor = UIColor.clear
  327. labelReadying?.textColor = UIColor.white
  328. labelReadying?.font = UIFont.systemFont(ofSize: 18.0)
  329. addSubview(labelReadying!)
  330. }
  331. addSubview(labelReadying!)
  332. activityView?.startAnimating()
  333. }
  334. func deviceStopReadying()
  335. {
  336. if activityView != nil
  337. {
  338. activityView?.stopAnimating()
  339. activityView?.removeFromSuperview()
  340. labelReadying?.removeFromSuperview()
  341. activityView = nil
  342. labelReadying = nil
  343. }
  344. }
  345. }