2
0

LBXScanView.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. //
  2. // LBXScanView.m
  3. //
  4. //
  5. // Created by lbxia on 15/11/15.
  6. // Copyright © 2015年 lbxia. All rights reserved.
  7. //
  8. #import "LBXScanView.h"
  9. NS_ASSUME_NONNULL_BEGIN
  10. @interface LBXScanView()
  11. //扫码区域各种参数
  12. @property (nonatomic, strong,nullable) LBXScanViewStyle* viewStyle;
  13. //扫码区域
  14. @property (nonatomic,assign)CGRect scanRetangleRect;
  15. //线条扫码动画封装
  16. @property (nonatomic,strong,nullable)LBXScanLineAnimation *scanLineAnimation;
  17. //网格扫码动画封装
  18. @property (nonatomic,strong,nullable)LBXScanNetAnimation *scanNetAnimation;
  19. //线条在中间位置,不移动
  20. @property (nonatomic,strong,nullable)UIImageView *scanLineStill;
  21. /**
  22. @brief 启动相机时 菊花等待
  23. */
  24. @property(nonatomic,strong,nullable)UIActivityIndicatorView* activityView;
  25. /**
  26. @brief 启动相机中的提示文字
  27. */
  28. @property(nonatomic,strong,nullable)UILabel *labelReadying;
  29. @end
  30. NS_ASSUME_NONNULL_END
  31. @implementation LBXScanView
  32. -(id)initWithFrame:(CGRect)frame style:(LBXScanViewStyle*)style
  33. {
  34. if (self = [super initWithFrame:frame])
  35. {
  36. self.viewStyle = style;
  37. self.backgroundColor = [UIColor clearColor];
  38. }
  39. return self;
  40. }
  41. - (void)drawRect:(CGRect)rect
  42. {
  43. [self drawScanRect];
  44. }
  45. - (void)startDeviceReadyingWithText:(NSString*)text
  46. {
  47. int XRetangleLeft = _viewStyle.xScanRetangleOffset;
  48. CGSize sizeRetangle = CGSizeMake(self.frame.size.width - XRetangleLeft*2, self.frame.size.width - XRetangleLeft*2);
  49. if (!_viewStyle.isNeedShowRetangle) {
  50. CGFloat w = sizeRetangle.width;
  51. CGFloat h = w / _viewStyle.whRatio;
  52. NSInteger hInt = (NSInteger)h;
  53. h = hInt;
  54. sizeRetangle = CGSizeMake(w, h);
  55. }
  56. //扫码区域Y轴最小坐标
  57. CGFloat YMinRetangle = self.frame.size.height / 2.0 - sizeRetangle.height/2.0 - _viewStyle.centerUpOffset;
  58. //设备启动状态提示
  59. if (!_activityView)
  60. {
  61. self.activityView = [[UIActivityIndicatorView alloc]init];
  62. [_activityView setActivityIndicatorViewStyle:UIActivityIndicatorViewStyleWhiteLarge];
  63. self.labelReadying = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, sizeRetangle.width, 30)];
  64. _labelReadying.backgroundColor = [UIColor clearColor];
  65. _labelReadying.textColor = [UIColor whiteColor];
  66. _labelReadying.font = [UIFont systemFontOfSize:18.];
  67. _labelReadying.text = text;
  68. [_labelReadying sizeToFit];
  69. CGRect frame = _labelReadying.frame;
  70. CGPoint centerPt = CGPointMake(self.frame.size.width/2 + 20, YMinRetangle + sizeRetangle.height/2);
  71. _labelReadying.bounds = CGRectMake(0, 0, frame.size.width,30);
  72. _labelReadying.center = centerPt;
  73. _activityView.bounds = CGRectMake(0, 0, 30, 30);
  74. if (text)
  75. _activityView.center = CGPointMake(centerPt.x - frame.size.width/2 - 24 , _labelReadying.center.y);
  76. else
  77. _activityView.center = CGPointMake(self.frame.size.width/2 , _labelReadying.center.y);
  78. [self addSubview:_activityView];
  79. [self addSubview:_labelReadying];
  80. [_activityView startAnimating];
  81. }
  82. }
  83. - (void)layoutSubviews
  84. {
  85. [super layoutSubviews];
  86. }
  87. - (void)stopDeviceReadying
  88. {
  89. if (_activityView) {
  90. [_activityView stopAnimating];
  91. [_activityView removeFromSuperview];
  92. [_labelReadying removeFromSuperview];
  93. self.activityView = nil;
  94. self.labelReadying = nil;
  95. }
  96. }
  97. /**
  98. * 开始扫描动画
  99. */
  100. - (void)startScanAnimation
  101. {
  102. switch (_viewStyle.anmiationStyle)
  103. {
  104. case LBXScanViewAnimationStyle_LineMove:
  105. {
  106. //线动画
  107. if (!_scanLineAnimation)
  108. self.scanLineAnimation = [[LBXScanLineAnimation alloc]init];
  109. [_scanLineAnimation startAnimatingWithRect:_scanRetangleRect
  110. InView:self
  111. Image:_viewStyle.animationImage];
  112. }
  113. break;
  114. case LBXScanViewAnimationStyle_NetGrid:
  115. {
  116. //网格动画
  117. if (!_scanNetAnimation)
  118. self.scanNetAnimation = [[LBXScanNetAnimation alloc]init];
  119. [_scanNetAnimation startAnimatingWithRect:_scanRetangleRect
  120. InView:self
  121. Image:_viewStyle.animationImage];
  122. }
  123. break;
  124. case LBXScanViewAnimationStyle_LineStill:
  125. {
  126. if (!_scanLineStill) {
  127. CGRect stillRect = CGRectMake(_scanRetangleRect.origin.x+20,
  128. _scanRetangleRect.origin.y + _scanRetangleRect.size.height/2,
  129. _scanRetangleRect.size.width-40,
  130. 2);
  131. _scanLineStill = [[UIImageView alloc]initWithFrame:stillRect];
  132. _scanLineStill.image = _viewStyle.animationImage;
  133. }
  134. [self addSubview:_scanLineStill];
  135. }
  136. default:
  137. break;
  138. }
  139. }
  140. /**
  141. * 结束扫描动画
  142. */
  143. - (void)stopScanAnimation
  144. {
  145. if (_scanLineAnimation) {
  146. [_scanLineAnimation stopAnimating];
  147. }
  148. if (_scanNetAnimation) {
  149. [_scanNetAnimation stopAnimating];
  150. }
  151. if (_scanLineStill) {
  152. [_scanLineStill removeFromSuperview];
  153. }
  154. }
  155. - (void)drawScanRect
  156. {
  157. int XRetangleLeft = _viewStyle.xScanRetangleOffset;
  158. CGSize sizeRetangle = CGSizeMake(self.frame.size.width - XRetangleLeft*2, self.frame.size.width - XRetangleLeft*2);
  159. //if (!_viewStyle.isScanRetangelSquare)
  160. if (_viewStyle.whRatio != 1)
  161. {
  162. CGFloat w = sizeRetangle.width;
  163. CGFloat h = w / _viewStyle.whRatio;
  164. NSInteger hInt = (NSInteger)h;
  165. h = hInt;
  166. sizeRetangle = CGSizeMake(w, h);
  167. }
  168. //扫码区域Y轴最小坐标
  169. CGFloat YMinRetangle = self.frame.size.height / 2.0 - sizeRetangle.height/2.0 - _viewStyle.centerUpOffset;
  170. CGFloat YMaxRetangle = YMinRetangle + sizeRetangle.height;
  171. CGFloat XRetangleRight = self.frame.size.width - XRetangleLeft;
  172. NSLog(@"frame:%@",NSStringFromCGRect(self.frame));
  173. CGContextRef context = UIGraphicsGetCurrentContext();
  174. //非扫码区域半透明
  175. {
  176. //设置非识别区域颜色
  177. const CGFloat *components = CGColorGetComponents(_viewStyle.notRecoginitonArea.CGColor);
  178. CGFloat red_notRecoginitonArea = components[0];
  179. CGFloat green_notRecoginitonArea = components[1];
  180. CGFloat blue_notRecoginitonArea = components[2];
  181. CGFloat alpa_notRecoginitonArea = components[3];
  182. CGContextSetRGBFillColor(context, red_notRecoginitonArea, green_notRecoginitonArea,
  183. blue_notRecoginitonArea, alpa_notRecoginitonArea);
  184. //填充矩形
  185. //扫码区域上面填充
  186. CGRect rect = CGRectMake(0, 0, self.frame.size.width, YMinRetangle);
  187. CGContextFillRect(context, rect);
  188. //扫码区域左边填充
  189. rect = CGRectMake(0, YMinRetangle, XRetangleLeft,sizeRetangle.height);
  190. CGContextFillRect(context, rect);
  191. //扫码区域右边填充
  192. rect = CGRectMake(XRetangleRight, YMinRetangle, XRetangleLeft,sizeRetangle.height);
  193. CGContextFillRect(context, rect);
  194. //扫码区域下面填充
  195. rect = CGRectMake(0, YMaxRetangle, self.frame.size.width,self.frame.size.height - YMaxRetangle);
  196. CGContextFillRect(context, rect);
  197. //执行绘画
  198. CGContextStrokePath(context);
  199. }
  200. if (_viewStyle.isNeedShowRetangle)
  201. {
  202. //中间画矩形(正方形)
  203. CGContextSetStrokeColorWithColor(context, _viewStyle.colorRetangleLine.CGColor);
  204. CGContextSetLineWidth(context, 1);
  205. CGContextAddRect(context, CGRectMake(XRetangleLeft, YMinRetangle, sizeRetangle.width, sizeRetangle.height));
  206. //CGContextMoveToPoint(context, XRetangleLeft, YMinRetangle);
  207. //CGContextAddLineToPoint(context, XRetangleLeft+sizeRetangle.width, YMinRetangle);
  208. CGContextStrokePath(context);
  209. }
  210. _scanRetangleRect = CGRectMake(XRetangleLeft, YMinRetangle, sizeRetangle.width, sizeRetangle.height);
  211. //画矩形框4格外围相框角
  212. //相框角的宽度和高度
  213. int wAngle = _viewStyle.photoframeAngleW;
  214. int hAngle = _viewStyle.photoframeAngleH;
  215. //4个角的 线的宽度
  216. CGFloat linewidthAngle = _viewStyle.photoframeLineW;// 经验参数:6和4
  217. //画扫码矩形以及周边半透明黑色坐标参数
  218. CGFloat diffAngle = 0.0f;
  219. //diffAngle = linewidthAngle / 2; //框外面4个角,与框有缝隙
  220. //diffAngle = linewidthAngle/2; //框4个角 在线上加4个角效果
  221. //diffAngle = 0;//与矩形框重合
  222. switch (_viewStyle.photoframeAngleStyle)
  223. {
  224. case LBXScanViewPhotoframeAngleStyle_Outer:
  225. {
  226. diffAngle = linewidthAngle/3;//框外面4个角,与框紧密联系在一起
  227. }
  228. break;
  229. case LBXScanViewPhotoframeAngleStyle_On:
  230. {
  231. diffAngle = 0;
  232. }
  233. break;
  234. case LBXScanViewPhotoframeAngleStyle_Inner:
  235. {
  236. diffAngle = -_viewStyle.photoframeLineW/2;
  237. }
  238. break;
  239. default:
  240. {
  241. diffAngle = linewidthAngle/3;
  242. }
  243. break;
  244. }
  245. CGContextSetStrokeColorWithColor(context, _viewStyle.colorAngle.CGColor);
  246. CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 1.0);
  247. // Draw them with a 2.0 stroke width so they are a bit more visible.
  248. CGContextSetLineWidth(context, linewidthAngle);
  249. //
  250. CGFloat leftX = XRetangleLeft - diffAngle;
  251. CGFloat topY = YMinRetangle - diffAngle;
  252. CGFloat rightX = XRetangleRight + diffAngle;
  253. CGFloat bottomY = YMaxRetangle + diffAngle;
  254. //左上角水平线
  255. CGContextMoveToPoint(context, leftX-linewidthAngle/2, topY);
  256. CGContextAddLineToPoint(context, leftX + wAngle, topY);
  257. //左上角垂直线
  258. CGContextMoveToPoint(context, leftX, topY-linewidthAngle/2);
  259. CGContextAddLineToPoint(context, leftX, topY+hAngle);
  260. //左下角水平线
  261. CGContextMoveToPoint(context, leftX-linewidthAngle/2, bottomY);
  262. CGContextAddLineToPoint(context, leftX + wAngle, bottomY);
  263. //左下角垂直线
  264. CGContextMoveToPoint(context, leftX, bottomY+linewidthAngle/2);
  265. CGContextAddLineToPoint(context, leftX, bottomY - hAngle);
  266. //右上角水平线
  267. CGContextMoveToPoint(context, rightX+linewidthAngle/2, topY);
  268. CGContextAddLineToPoint(context, rightX - wAngle, topY);
  269. //右上角垂直线
  270. CGContextMoveToPoint(context, rightX, topY-linewidthAngle/2);
  271. CGContextAddLineToPoint(context, rightX, topY + hAngle);
  272. //右下角水平线
  273. CGContextMoveToPoint(context, rightX+linewidthAngle/2, bottomY);
  274. CGContextAddLineToPoint(context, rightX - wAngle, bottomY);
  275. //右下角垂直线
  276. CGContextMoveToPoint(context, rightX, bottomY+linewidthAngle/2);
  277. CGContextAddLineToPoint(context, rightX, bottomY - hAngle);
  278. CGContextStrokePath(context);
  279. }
  280. //根据矩形区域,获取识别区域
  281. + (CGRect)getScanRectWithPreView:(UIView*)view style:(LBXScanViewStyle*)style
  282. {
  283. int XRetangleLeft = style.xScanRetangleOffset;
  284. CGSize sizeRetangle = CGSizeMake(view.frame.size.width - XRetangleLeft*2, view.frame.size.width - XRetangleLeft*2);
  285. if (style.whRatio != 1)
  286. {
  287. CGFloat w = sizeRetangle.width;
  288. CGFloat h = w / style.whRatio;
  289. NSInteger hInt = (NSInteger)h;
  290. h = hInt;
  291. sizeRetangle = CGSizeMake(w, h);
  292. }
  293. //扫码区域Y轴最小坐标
  294. CGFloat YMinRetangle = view.frame.size.height / 2.0 - sizeRetangle.height/2.0 - style.centerUpOffset;
  295. //扫码区域坐标
  296. CGRect cropRect = CGRectMake(XRetangleLeft, YMinRetangle, sizeRetangle.width, sizeRetangle.height);
  297. //计算兴趣区域
  298. CGRect rectOfInterest;
  299. //ref:http://www.cocoachina.com/ios/20141225/10763.html
  300. CGSize size = view.bounds.size;
  301. CGFloat p1 = size.height/size.width;
  302. CGFloat p2 = 1920./1080.; //使用了1080p的图像输出
  303. if (p1 < p2) {
  304. CGFloat fixHeight = size.width * 1920. / 1080.;
  305. CGFloat fixPadding = (fixHeight - size.height)/2;
  306. rectOfInterest = CGRectMake((cropRect.origin.y + fixPadding)/fixHeight,
  307. cropRect.origin.x/size.width,
  308. cropRect.size.height/fixHeight,
  309. cropRect.size.width/size.width);
  310. } else {
  311. CGFloat fixWidth = size.height * 1080. / 1920.;
  312. CGFloat fixPadding = (fixWidth - size.width)/2;
  313. rectOfInterest = CGRectMake(cropRect.origin.y/size.height,
  314. (cropRect.origin.x + fixPadding)/fixWidth,
  315. cropRect.size.height/size.height,
  316. cropRect.size.width/fixWidth);
  317. }
  318. return rectOfInterest;
  319. }
  320. //根据矩形区域,获取识别区域
  321. + (CGRect)getZXingScanRectWithPreView:(UIView*)view style:(LBXScanViewStyle*)style
  322. {
  323. int XRetangleLeft = style.xScanRetangleOffset;
  324. CGSize sizeRetangle = CGSizeMake(view.frame.size.width - XRetangleLeft*2, view.frame.size.width - XRetangleLeft*2);
  325. if (style.whRatio != 1)
  326. {
  327. CGFloat w = sizeRetangle.width;
  328. CGFloat h = w / style.whRatio;
  329. NSInteger hInt = (NSInteger)h;
  330. h = hInt;
  331. sizeRetangle = CGSizeMake(w, h);
  332. }
  333. //扫码区域Y轴最小坐标
  334. CGFloat YMinRetangle = view.frame.size.height / 2.0 - sizeRetangle.height/2.0 - style.centerUpOffset;
  335. XRetangleLeft = XRetangleLeft/view.frame.size.width * 1080;
  336. YMinRetangle = YMinRetangle / view.frame.size.height * 1920;
  337. CGFloat width = sizeRetangle.width / view.frame.size.width * 1080;
  338. CGFloat height = sizeRetangle.height / view.frame.size.height * 1920;
  339. //扫码区域坐标
  340. CGRect cropRect = CGRectMake(XRetangleLeft, YMinRetangle, width,height);
  341. return cropRect;
  342. }
  343. @end