CDZQRScanView.m 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  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.maskView01];
  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. [_deviceInput.device lockForConfiguration:nil];
  185. _deviceInput.device.torchMode = torch;
  186. [_deviceInput.device unlockForConfiguration];
  187. }
  188. #pragma mark - bezierPath
  189. - (void)addScanBorderLine{
  190. 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);
  191. UIBezierPath *scanBezierPath = [UIBezierPath bezierPathWithRect:borderRect];
  192. CAShapeLayer *lineLayer = [CAShapeLayer layer];
  193. lineLayer.path = scanBezierPath.CGPath;
  194. lineLayer.lineWidth = borderLineWidth;
  195. lineLayer.strokeColor = self.borderLineColor.CGColor;
  196. lineLayer.fillColor = [UIColor clearColor].CGColor;
  197. [self.layer addSublayer:lineLayer];
  198. }
  199. - (void)addCornerLines{
  200. CAShapeLayer *lineLayer = [CAShapeLayer layer];
  201. lineLayer.lineWidth = cornerLineWidth;
  202. lineLayer.strokeColor = self.cornerLineColor.CGColor;
  203. lineLayer.fillColor = [UIColor clearColor].CGColor;
  204. CGFloat halfLineLong = self.scanRect.size.width / 17;
  205. UIBezierPath *lineBezierPath = [UIBezierPath bezierPath];
  206. CGFloat spacing = cornerLineWidth/2;
  207. CGPoint leftUpPoint = (CGPoint){self.scanRect.origin.x + spacing ,self.scanRect.origin.y + spacing};
  208. [lineBezierPath moveToPoint:(CGPoint){leftUpPoint.x,leftUpPoint.y + halfLineLong}];
  209. [lineBezierPath addLineToPoint:leftUpPoint];
  210. [lineBezierPath addLineToPoint:(CGPoint){leftUpPoint.x + halfLineLong,leftUpPoint.y}];
  211. lineLayer.path = lineBezierPath.CGPath;
  212. [self.layer addSublayer:lineLayer];
  213. CGPoint leftDownPoint = (CGPoint){self.scanRect.origin.x + spacing,self.scanRect.origin.y + self.scanRect.size.height - spacing};
  214. [lineBezierPath moveToPoint:(CGPoint){leftDownPoint.x,leftDownPoint.y - halfLineLong}];
  215. [lineBezierPath addLineToPoint:leftDownPoint];
  216. [lineBezierPath addLineToPoint:(CGPoint){leftDownPoint.x + halfLineLong,leftDownPoint.y}];
  217. lineLayer.path = lineBezierPath.CGPath;
  218. [self.layer addSublayer:lineLayer];
  219. CGPoint rightUpPoint = (CGPoint){self.scanRect.origin.x + self.scanRect.size.width - spacing,self.scanRect.origin.y + spacing};
  220. [lineBezierPath moveToPoint:(CGPoint){rightUpPoint.x - halfLineLong,rightUpPoint.y}];
  221. [lineBezierPath addLineToPoint:rightUpPoint];
  222. [lineBezierPath addLineToPoint:(CGPoint){rightUpPoint.x,rightUpPoint.y + halfLineLong}];
  223. lineLayer.path = lineBezierPath.CGPath;
  224. [self.layer addSublayer:lineLayer];
  225. CGPoint rightDownPoint = (CGPoint){self.scanRect.origin.x + self.scanRect.size.width - spacing,self.scanRect.origin.y + self.scanRect.size.height - spacing};
  226. [lineBezierPath moveToPoint:(CGPoint){rightDownPoint.x - halfLineLong,rightDownPoint.y}];
  227. [lineBezierPath addLineToPoint:rightDownPoint];
  228. [lineBezierPath addLineToPoint:(CGPoint){rightDownPoint.x,rightDownPoint.y - halfLineLong}];
  229. lineLayer.path = lineBezierPath.CGPath;
  230. [self.layer addSublayer:lineLayer];
  231. }
  232. #pragma mark - AVCaptureMetadataOutputObjectsDelegate
  233. - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray<AVMetadataMachineReadableCodeObject *> *)metadataObjects fromConnection:(AVCaptureConnection *)connection{
  234. if (metadataObjects.count == 0) {
  235. return;
  236. }
  237. NSString *result = [metadataObjects.firstObject stringValue];
  238. if ([self.delegate respondsToSelector:@selector(scanView:pickUpMessage:)]) {
  239. [self.delegate scanView:self pickUpMessage:result];
  240. }
  241. }
  242. #pragma mark - getter&setter
  243. - (CGRect)scanRect{
  244. if (CGRectIsEmpty(_scanRect)) {
  245. CGSize scanSize = CGSizeMake(self.frame.size.width * 3/4, self.frame.size.width * 3/4);
  246. _scanRect = CGRectMake((self.frame.size.width - scanSize.width)/2, (self.frame.size.height - scanSize.height)/2, scanSize.width, scanSize.height);
  247. }
  248. return _scanRect;
  249. }
  250. - (UIView *)maskView01{
  251. if (!_maskView01) {
  252. _maskView01 = [[UIView alloc]initWithFrame:self.bounds];
  253. _maskView01.backgroundColor = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.5];
  254. UIBezierPath *fullBezierPath = [UIBezierPath bezierPathWithRect:self.bounds];
  255. UIBezierPath *scanBezierPath = [UIBezierPath bezierPathWithRect:self.scanRect];
  256. [fullBezierPath appendPath:[scanBezierPath bezierPathByReversingPath]];
  257. CAShapeLayer *shapeLayer = [CAShapeLayer layer];
  258. shapeLayer.path = fullBezierPath.CGPath;
  259. _maskView01.layer.mask = shapeLayer;
  260. }
  261. return _maskView01;
  262. }
  263. - (UIView *)middleView{
  264. if (!_middleView) {
  265. _middleView = [[UIView alloc]initWithFrame:self.scanRect];
  266. _middleView.clipsToBounds = YES;
  267. }
  268. return _middleView;
  269. }
  270. - (UIView *)scanLine{
  271. if (!_scanLine) {
  272. // _scanLine = [[UIView alloc]initWithFrame:CGRectMake(0,0, self.scanRect.size.width, scanLineWidth)];
  273. // _scanLine.hidden = YES;
  274. // CAGradientLayer *gradient = [CAGradientLayer layer];
  275. // gradient.startPoint = CGPointMake(0.5, 0);
  276. // gradient.endPoint = CGPointMake(0.5, 1);
  277. // gradient.frame = _scanLine.layer.bounds;
  278. // gradient.colors = @[(__bridge id)[self.scanLineColor colorWithAlphaComponent:0].CGColor,(__bridge id)[self.scanLineColor colorWithAlphaComponent:0.4f].CGColor,(__bridge id)self.scanLineColor.CGColor];
  279. // gradient.locations = @[@0,@0.96,@0.97];
  280. // [_scanLine.layer addSublayer:gradient];
  281. UIImageView *scanLine = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, self.middleView.size.width, self.middleView.size.height)];
  282. scanLine.hidden = YES;
  283. scanLine.image = [UIImage imageNamed:@"qrcode_scan_part_net"];
  284. _scanLine = scanLine;
  285. }
  286. return _scanLine;
  287. }
  288. - (UIColor *)cornerLineColor{
  289. if (!_cornerLineColor) {
  290. _cornerLineColor = [UIColor orangeColor];
  291. }
  292. return _cornerLineColor;
  293. }
  294. - (UIColor *)borderLineColor{
  295. if (!_borderLineColor) {
  296. _borderLineColor = [UIColor whiteColor];
  297. }
  298. return _borderLineColor;
  299. }
  300. - (UIColor *)scanLineColor{
  301. if (!_scanLineColor) {
  302. _scanLineColor = [UIColor orangeColor];
  303. }
  304. return _scanLineColor;
  305. }
  306. @end