// Copyright © 2016 Open Whisper Systems. All rights reserved. #import "OWSQRCodeScanningViewController.h" #import "UIColor+OWS.h" //#import @interface OWSQRCodeScanningViewController () @property (nonatomic) BOOL captureEnabled; @property (atomic, strong) ZXCapture *capture; @property UIView *maskingView; @property CALayer *maskingLayer; @end @implementation OWSQRCodeScanningViewController - (void)dealloc { [self.capture.layer removeFromSuperlayer]; } - (instancetype)init { self = [super init]; if (!self) { return self; } _captureEnabled = NO; return self; } - (instancetype)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (!self) { return self; } _captureEnabled = NO; return self; } - (void)viewDidLoad { [super viewDidLoad]; self.maskingView = [[UIView alloc] initWithFrame:self.view.frame]; [self.view addSubview:self.maskingView]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; if (self.captureEnabled) { [self startCapture]; } } - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; [self layoutMaskingView]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; [self stopCapture]; } - (void)layoutMaskingView { self.maskingView.frame = self.view.frame; if (self.maskingLayer) { [self.maskingLayer removeFromSuperlayer]; } self.maskingLayer = [self buildCircularMaskingLayer]; [self.maskingView.layer addSublayer:self.maskingLayer]; } - (void)startCapture { self.captureEnabled = YES; if (!self.capture) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ self.capture = [[ZXCapture alloc] init]; self.capture.camera = self.capture.back; self.capture.focusMode = AVCaptureFocusModeContinuousAutoFocus; self.capture.layer.frame = self.view.frame; self.capture.delegate = self; dispatch_async(dispatch_get_main_queue(), ^{ [self.view.layer addSublayer:self.capture.layer]; [self.view bringSubviewToFront:self.maskingView]; [self.capture start]; }); }); } else { [self.capture start]; } } - (void)stopCapture { self.captureEnabled = NO; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self.capture stop]; }); } - (CAShapeLayer *)buildCircularMaskingLayer { // Add a circular mask UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height) cornerRadius:0]; CGFloat verticalMargin = 8.0; CGFloat radius = self.view.frame.size.height / 2.0f - verticalMargin; // Center the circle's bounding rectangle CGFloat horizontalMargin = (self.view.frame.size.width - 2.0f * radius) / 2.0f; UIBezierPath *circlePath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(horizontalMargin, verticalMargin, 2.0f * radius, 2.0f * radius) cornerRadius:radius]; [path appendPath:circlePath]; [path setUsesEvenOddFillRule:YES]; CAShapeLayer *fillLayer = [CAShapeLayer layer]; fillLayer.path = path.CGPath; fillLayer.fillRule = kCAFillRuleEvenOdd; fillLayer.fillColor = [UIColor grayColor].CGColor; fillLayer.opacity = 0.5; return fillLayer; } - (void)captureResult:(ZXCapture *)capture result:(ZXResult *)result { if (!self.captureEnabled) return; [self stopCapture]; // TODO bounding rectangle // Vibrate AudioServicesPlaySystemSound(kSystemSoundID_Vibrate); if (self.scanDelegate) { if ([self.scanDelegate respondsToSelector:@selector(controller:didDetectQRCodeWithData:)]) { DDLogInfo(@"%@ Scanned Data Code.", self.tag); ZXByteArray *byteArray = result.resultMetadata[@(kResultMetadataTypeByteSegments)][0]; NSData *decodedData = [NSData dataWithBytes:byteArray.array length:byteArray.length]; [self.scanDelegate controller:self didDetectQRCodeWithData:decodedData]; } if ([self.scanDelegate respondsToSelector:@selector(controller:didDetectQRCodeWithString:)]) { DDLogInfo(@"%@ Scanned String Code.", self.tag); [self.scanDelegate controller:self didDetectQRCodeWithString:result.text]; } } } + (NSString *)tag { return [NSString stringWithFormat:@"[%@]", self.class]; } - (NSString *)tag { return self.class.tag; } @end