CDZQRScanView.m 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. //
  2. // ONEQRScanView.m
  3. // Pods
  4. //
  5. // Created by Nemocdz on 2017/5/2.
  6. //
  7. //
  8. #import "CDZQRScanView.h"
  9. #import <AVFoundation/AVFoundation.h>
  10. static CGFloat scanTime = 2.0;
  11. static CGFloat borderLineWidth = 0.5;
  12. static CGFloat cornerLineWidth = 2;//1.5
  13. //static CGFloat scanLineWidth = 42;
  14. static NSString *const scanLineAnimationName = @"scanLineAnimation";
  15. @interface CDZQRScanView ()<AVCaptureMetadataOutputObjectsDelegate>
  16. @property (nonatomic, strong) AVCaptureDevice *device;
  17. @property (nonatomic, strong) AVCaptureDeviceInput *deviceInput;
  18. @property (nonatomic, strong) AVCaptureMetadataOutput *dataOutput;
  19. @property (nonatomic, strong) AVCaptureSession *session;
  20. @property (nonatomic, strong) UIView *scanLine;
  21. @property (nonatomic, strong) UIView *middleView;
  22. @property (nonatomic, strong) AVCaptureVideoPreviewLayer *previewLayer;
  23. @end
  24. @implementation CDZQRScanView
  25. - (instancetype)initWithFrame:(CGRect)frame{
  26. if (self = [super initWithFrame:frame]) {
  27. self.backgroundColor = [UIColor blackColor];
  28. _showScanLine = YES;
  29. _showCornerLine = YES;
  30. _showBorderLine = NO;
  31. }
  32. return self;
  33. }
  34. - (void)configCameraAndStart{
  35. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  36. self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
  37. NSError *error;
  38. self.deviceInput = [AVCaptureDeviceInput deviceInputWithDevice:self.device error:&error];
  39. if (error) {
  40. NSLog(@"%@",error);
  41. }
  42. self.dataOutput = [[AVCaptureMetadataOutput alloc] init];
  43. [self.dataOutput setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
  44. self.session = [[AVCaptureSession alloc] init];
  45. if ([self.device supportsAVCaptureSessionPreset:AVCaptureSessionPreset1920x1080]) {
  46. [self.session setSessionPreset:AVCaptureSessionPreset1920x1080];
  47. }
  48. else{
  49. [self.session setSessionPreset:AVCaptureSessionPresetHigh];
  50. }
  51. if ([self.session canAddInput:self.deviceInput]){
  52. [self.session addInput:self.deviceInput];
  53. }
  54. if ([self.session canAddOutput:self.dataOutput]){
  55. [self.session addOutput:self.dataOutput];
  56. }
  57. if (![self.dataOutput.availableMetadataObjectTypes containsObject:AVMetadataObjectTypeQRCode]) {
  58. NSLog(@"The camera unsupport for QRCode.");
  59. }
  60. self.dataOutput.metadataObjectTypes = @[AVMetadataObjectTypeQRCode];
  61. dispatch_async(dispatch_get_main_queue(), ^{
  62. self.previewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.session];
  63. self.previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
  64. self.previewLayer.frame = self.frame;
  65. [self.layer insertSublayer:self.previewLayer atIndex:0];
  66. [self.session startRunning];
  67. self.dataOutput.rectOfInterest = [self.previewLayer metadataOutputRectOfInterestForRect:self.scanRect];
  68. NSLog(@"%@",NSStringFromCGRect(self.dataOutput.rectOfInterest));
  69. [self showScanLine:self.isShowScanLine];
  70. });
  71. });
  72. }
  73. - (void)showScanLine:(BOOL)showScanLine{
  74. if (showScanLine) {
  75. [self addScanLineAnimation];
  76. }
  77. else{
  78. [self removeScanLineAnimation];
  79. }
  80. }
  81. - (void)startScanning{
  82. if (![self statusCheck]) {
  83. return;
  84. }
  85. if (!self.session) {
  86. [self setupViews];
  87. [self configCameraAndStart];
  88. return;
  89. }
  90. if (self.session.isRunning){
  91. return;
  92. }
  93. [self.session startRunning];
  94. [self showScanLine:self.isShowScanLine];
  95. }
  96. - (void)stopScanning{
  97. if (!self.session.isRunning){
  98. return;
  99. }
  100. [self.session stopRunning];
  101. [self showScanLine:NO];
  102. }
  103. - (BOOL)statusCheck{
  104. if (![self isCameraAvailable]){
  105. [self showWarn:@"设备无相机——设备无相机功能,无法进行扫描"];
  106. return NO;
  107. }
  108. if (![self isRearCameraAvailable] && ![self isFrontCameraAvailable]) {
  109. [self showWarn:@"设备相机错误——无法启用相机,请检查"];
  110. return NO;
  111. }
  112. if (![self isCameraAuthStatusCorrect]) {
  113. [self showWarn:@"未打开相机权限 ——请在“设置-隐私-相机”选项中,允许优易学车访问你的相机"];
  114. return NO;
  115. }
  116. return YES;
  117. }
  118. - (void)showWarn:(NSString *)message{
  119. UIAlertController *alert = [UIAlertController alertControllerWithTitle:nil message:message preferredStyle:UIAlertControllerStyleAlert];
  120. UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"好" style:UIAlertActionStyleCancel handler:nil];
  121. [alert addAction:cancelAction];
  122. [(UIViewController *)self.delegate presentViewController:alert animated:YES completion:nil];
  123. }
  124. - (void)setupViews{
  125. [self addSubview:self.middleView];
  126. [self.middleView addSubview:self.scanLine];
  127. [self addSubview:self.maskView];
  128. if (self.isShowCornerLine) {
  129. [self addCornerLines];
  130. }
  131. if (self.isShowBorderLine){
  132. [self addScanBorderLine];
  133. }
  134. }
  135. - (BOOL)isCameraAvailable{
  136. return [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];
  137. }
  138. - (BOOL)isFrontCameraAvailable{
  139. return [UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront];
  140. }
  141. - (BOOL)isRearCameraAvailable{
  142. return [UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear];
  143. }
  144. - (BOOL)isCameraAuthStatusCorrect{
  145. AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
  146. NSLog(@"AVAuthorizationStatus: %ld",authStatus);
  147. if (authStatus == AVAuthorizationStatusAuthorized || authStatus == AVAuthorizationStatusNotDetermined) {
  148. return YES;
  149. }
  150. return NO;
  151. }
  152. - (void)addScanLineAnimation{
  153. self.scanLine.hidden = NO;
  154. CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.translation.y"];
  155. animation.fromValue = @(- CGRectGetHeight(self.scanLine.frame));
  156. animation.toValue = @(self.scanRect.size.height - CGRectGetHeight(self.scanLine.frame));
  157. animation.duration = scanTime;
  158. animation.repeatCount = OPEN_MAX;
  159. animation.fillMode = kCAFillModeForwards;
  160. animation.removedOnCompletion = NO;
  161. animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
  162. [self.scanLine.layer addAnimation:animation forKey:scanLineAnimationName];
  163. }
  164. - (void)removeScanLineAnimation{
  165. [self.scanLine.layer removeAnimationForKey:scanLineAnimationName];
  166. self.scanLine.hidden = YES;
  167. }
  168. #pragma mark - flash
  169. - (void)changeTorch
  170. {
  171. AVCaptureTorchMode torch = self.deviceInput.device.torchMode;
  172. switch (_deviceInput.device.torchMode) {
  173. case AVCaptureTorchModeAuto:
  174. break;
  175. case AVCaptureTorchModeOff:
  176. torch = AVCaptureTorchModeOn;
  177. break;
  178. case AVCaptureTorchModeOn:
  179. torch = AVCaptureTorchModeOff;
  180. break;
  181. default:
  182. break;
  183. }
  184. // 更改设置的时候必须先锁定设备,修改完后再解锁,否则崩溃
  185. [_deviceInput.device lockForConfiguration:nil];
  186. // 判断设备是否支持闪光灯
  187. if ([_deviceInput.device hasFlash]) {
  188. _deviceInput.device.torchMode = torch;
  189. }else {
  190. ShowMsg(@"该设备不支持闪光灯");
  191. }
  192. // 修改完毕解锁
  193. [_deviceInput.device unlockForConfiguration];
  194. }
  195. #pragma mark - bezierPath
  196. - (void)addScanBorderLine{
  197. CGRect borderRect = CGRectMake(self.scanRect.origin.x + borderLineWidth, self.scanRect.origin.y + borderLineWidth, self.scanRect.size.width - 2*borderLineWidth, self.scanRect.size.height - 2*borderLineWidth);
  198. UIBezierPath *scanBezierPath = [UIBezierPath bezierPathWithRect:borderRect];
  199. CAShapeLayer *lineLayer = [CAShapeLayer layer];
  200. lineLayer.path = scanBezierPath.CGPath;
  201. lineLayer.lineWidth = borderLineWidth;
  202. lineLayer.strokeColor = self.borderLineColor.CGColor;
  203. lineLayer.fillColor = [UIColor clearColor].CGColor;
  204. [self.layer addSublayer:lineLayer];
  205. }
  206. - (void)addCornerLines{
  207. CAShapeLayer *lineLayer = [CAShapeLayer layer];
  208. lineLayer.lineWidth = cornerLineWidth;
  209. lineLayer.strokeColor = self.cornerLineColor.CGColor;
  210. lineLayer.fillColor = [UIColor clearColor].CGColor;
  211. CGFloat halfLineLong = self.scanRect.size.width / 17;
  212. UIBezierPath *lineBezierPath = [UIBezierPath bezierPath];
  213. CGFloat spacing = cornerLineWidth/2;
  214. CGPoint leftUpPoint = (CGPoint){self.scanRect.origin.x + spacing ,self.scanRect.origin.y + spacing};
  215. [lineBezierPath moveToPoint:(CGPoint){leftUpPoint.x,leftUpPoint.y + halfLineLong}];
  216. [lineBezierPath addLineToPoint:leftUpPoint];
  217. [lineBezierPath addLineToPoint:(CGPoint){leftUpPoint.x + halfLineLong,leftUpPoint.y}];
  218. lineLayer.path = lineBezierPath.CGPath;
  219. [self.layer addSublayer:lineLayer];
  220. CGPoint leftDownPoint = (CGPoint){self.scanRect.origin.x + spacing,self.scanRect.origin.y + self.scanRect.size.height - spacing};
  221. [lineBezierPath moveToPoint:(CGPoint){leftDownPoint.x,leftDownPoint.y - halfLineLong}];
  222. [lineBezierPath addLineToPoint:leftDownPoint];
  223. [lineBezierPath addLineToPoint:(CGPoint){leftDownPoint.x + halfLineLong,leftDownPoint.y}];
  224. lineLayer.path = lineBezierPath.CGPath;
  225. [self.layer addSublayer:lineLayer];
  226. CGPoint rightUpPoint = (CGPoint){self.scanRect.origin.x + self.scanRect.size.width - spacing,self.scanRect.origin.y + spacing};
  227. [lineBezierPath moveToPoint:(CGPoint){rightUpPoint.x - halfLineLong,rightUpPoint.y}];
  228. [lineBezierPath addLineToPoint:rightUpPoint];
  229. [lineBezierPath addLineToPoint:(CGPoint){rightUpPoint.x,rightUpPoint.y + halfLineLong}];
  230. lineLayer.path = lineBezierPath.CGPath;
  231. [self.layer addSublayer:lineLayer];
  232. CGPoint rightDownPoint = (CGPoint){self.scanRect.origin.x + self.scanRect.size.width - spacing,self.scanRect.origin.y + self.scanRect.size.height - spacing};
  233. [lineBezierPath moveToPoint:(CGPoint){rightDownPoint.x - halfLineLong,rightDownPoint.y}];
  234. [lineBezierPath addLineToPoint:rightDownPoint];
  235. [lineBezierPath addLineToPoint:(CGPoint){rightDownPoint.x,rightDownPoint.y - halfLineLong}];
  236. lineLayer.path = lineBezierPath.CGPath;
  237. [self.layer addSublayer:lineLayer];
  238. }
  239. #pragma mark - AVCaptureMetadataOutputObjectsDelegate
  240. - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray<AVMetadataMachineReadableCodeObject *> *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
  241. if (metadataObjects.count == 0) {
  242. return;
  243. }
  244. NSString *result = [metadataObjects.firstObject stringValue];
  245. if ([self.delegate respondsToSelector:@selector(scanView:pickUpMessage:)]) {
  246. [self.delegate scanView:self pickUpMessage:result];
  247. }
  248. }
  249. #pragma mark - getter&setter
  250. - (CGRect)scanRect{
  251. if (CGRectIsEmpty(_scanRect)) {
  252. CGSize scanSize = CGSizeMake(self.frame.size.width * 3/4, self.frame.size.width * 3/4);
  253. _scanRect = CGRectMake((self.frame.size.width - scanSize.width)/2, (self.frame.size.height - scanSize.height)/2, scanSize.width, scanSize.height);
  254. }
  255. return _scanRect;
  256. }
  257. - (UIView *)maskView{
  258. if (!_maskView) {
  259. _maskView = [[UIView alloc]initWithFrame:self.bounds];
  260. _maskView.backgroundColor = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.5];
  261. UIBezierPath *fullBezierPath = [UIBezierPath bezierPathWithRect:self.bounds];
  262. UIBezierPath *scanBezierPath = [UIBezierPath bezierPathWithRect:self.scanRect];
  263. [fullBezierPath appendPath:[scanBezierPath bezierPathByReversingPath]];
  264. CAShapeLayer *shapeLayer = [CAShapeLayer layer];
  265. shapeLayer.path = fullBezierPath.CGPath;
  266. _maskView.layer.mask = shapeLayer;
  267. }
  268. return _maskView;
  269. }
  270. - (UIView *)middleView{
  271. if (!_middleView) {
  272. _middleView = [[UIView alloc]initWithFrame:self.scanRect];
  273. _middleView.clipsToBounds = YES;
  274. }
  275. return _middleView;
  276. }
  277. - (UIView *)scanLine{
  278. if (!_scanLine) {
  279. // _scanLine = [[UIView alloc]initWithFrame:CGRectMake(0,0, self.scanRect.size.width, scanLineWidth)];
  280. // _scanLine.hidden = YES;
  281. // CAGradientLayer *gradient = [CAGradientLayer layer];
  282. // gradient.startPoint = CGPointMake(0.5, 0);
  283. // gradient.endPoint = CGPointMake(0.5, 1);
  284. // gradient.frame = _scanLine.layer.bounds;
  285. // gradient.colors = @[(__bridge id)[self.scanLineColor colorWithAlphaComponent:0].CGColor,(__bridge id)[self.scanLineColor colorWithAlphaComponent:0.4f].CGColor,(__bridge id)self.scanLineColor.CGColor];
  286. // gradient.locations = @[@0,@0.96,@0.97];
  287. // [_scanLine.layer addSublayer:gradient];
  288. UIImageView *scanLine = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, self.middleView.size.width, self.middleView.size.height)];
  289. scanLine.hidden = YES;
  290. scanLine.image = [UIImage imageNamed:@"qrcode_scan_part_net"];
  291. _scanLine = scanLine;
  292. }
  293. return _scanLine;
  294. }
  295. - (UIColor *)cornerLineColor{
  296. if (!_cornerLineColor) {
  297. _cornerLineColor = [UIColor orangeColor];
  298. }
  299. return _cornerLineColor;
  300. }
  301. - (UIColor *)borderLineColor{
  302. if (!_borderLineColor) {
  303. _borderLineColor = [UIColor whiteColor];
  304. }
  305. return _borderLineColor;
  306. }
  307. - (UIColor *)scanLineColor{
  308. if (!_scanLineColor) {
  309. _scanLineColor = [UIColor orangeColor];
  310. }
  311. return _scanLineColor;
  312. }
  313. @end