From d66bca2ea6f59c7fd6185ed67b41c58eb20ea029 Mon Sep 17 00:00:00 2001 From: Matt Venables Date: Thu, 28 Aug 2014 12:20:57 -0400 Subject: [PATCH 01/30] Only process metadata if resultBlock is provided --- Classes/ios/Scanners/MTBBarcodeScanner.m | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Classes/ios/Scanners/MTBBarcodeScanner.m b/Classes/ios/Scanners/MTBBarcodeScanner.m index fdff745..0f4adda 100644 --- a/Classes/ios/Scanners/MTBBarcodeScanner.m +++ b/Classes/ios/Scanners/MTBBarcodeScanner.m @@ -180,14 +180,14 @@ - (BOOL)isScanning { #pragma mark - AVCaptureMetadataOutputObjects Delegate - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection { - NSMutableArray *codes = [[NSMutableArray alloc] init]; - for (AVMetadataObject *metaData in metadataObjects) { - AVMetadataMachineReadableCodeObject *barCodeObject = (AVMetadataMachineReadableCodeObject *)[self.capturePreviewLayer transformedMetadataObjectForMetadataObject:(AVMetadataMachineReadableCodeObject *)metaData]; - if (barCodeObject) { - [codes addObject:barCodeObject]; - } - } if (self.resultBlock) { + NSMutableArray *codes = [[NSMutableArray alloc] init]; + for (AVMetadataObject *metaData in metadataObjects) { + AVMetadataMachineReadableCodeObject *barCodeObject = (AVMetadataMachineReadableCodeObject *)[self.capturePreviewLayer transformedMetadataObjectForMetadataObject:(AVMetadataMachineReadableCodeObject *)metaData]; + if (barCodeObject) { + [codes addObject:barCodeObject]; + } + } self.resultBlock(codes); } } From 9515cb5e830a5419cf91b959da0e8685da3b8bcb Mon Sep 17 00:00:00 2001 From: Thiemo Klein Date: Tue, 28 Jul 2015 09:52:01 +0200 Subject: [PATCH 02/30] Fix a strange layout bug when embedded in a tabbarcontroller (Because statusBarOrientation gives a wrong value) --- Classes/ios/Scanners/MTBBarcodeScanner.h | 11 +++++++ Classes/ios/Scanners/MTBBarcodeScanner.m | 38 +++++++++++++++++++++--- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/Classes/ios/Scanners/MTBBarcodeScanner.h b/Classes/ios/Scanners/MTBBarcodeScanner.h index e764dc0..f55e7af 100644 --- a/Classes/ios/Scanners/MTBBarcodeScanner.h +++ b/Classes/ios/Scanners/MTBBarcodeScanner.h @@ -22,6 +22,17 @@ typedef NS_ENUM(NSUInteger, MTBCamera) { */ @property (nonatomic, assign) MTBCamera camera; +/** + * Use UIDeviceOrientation instead of UIInterfaceOrientation for changing the camera view to the correct orientation + * Set to YES, if a strange layout bug occurs. + */ +@property (nonatomic, assign) BOOL shouldUseUIDeviceOrientation; + +/** + * Use this BOOL only if shouldUseUIDeviceOrientation is set to YES + */ +@property (nonatomic, assign) BOOL shouldIgnoreFaceDownDeviceOrientation; + /** * Initialize a scanner that will feed the camera input * into the given UIView. diff --git a/Classes/ios/Scanners/MTBBarcodeScanner.m b/Classes/ios/Scanners/MTBBarcodeScanner.m index 5d868ca..253707a 100644 --- a/Classes/ios/Scanners/MTBBarcodeScanner.m +++ b/Classes/ios/Scanners/MTBBarcodeScanner.m @@ -283,10 +283,25 @@ - (void)handleDeviceOrientationDidChangeNotification:(NSNotification *)notificat } - (void)refreshVideoOrientation { - UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; - self.capturePreviewLayer.frame = self.previewView.bounds; - if ([self.capturePreviewLayer.connection isVideoOrientationSupported]) { - self.capturePreviewLayer.connection.videoOrientation = [self captureOrientationForInterfaceOrientation:orientation]; + if (self.shouldUseUIDeviceOrientation) { + UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation]; + + if (orientation == UIDeviceOrientationPortraitUpsideDown && self.shouldIgnoreFaceDownDeviceOrientation) { + return; + } else { + self.capturePreviewLayer.frame = self.previewView.bounds; + if ([self.capturePreviewLayer.connection isVideoOrientationSupported]) { + self.capturePreviewLayer.connection.videoOrientation = [self captureOrientationForDeviceOrientation:orientation]; + } + } + + } else { + UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; + + self.capturePreviewLayer.frame = self.previewView.bounds; + if ([self.capturePreviewLayer.connection isVideoOrientationSupported]) { + self.capturePreviewLayer.connection.videoOrientation = [self captureOrientationForInterfaceOrientation:orientation]; + } } } @@ -305,6 +320,21 @@ - (AVCaptureVideoOrientation)captureOrientationForInterfaceOrientation:(UIInterf } } +- (AVCaptureVideoOrientation)captureOrientationForDeviceOrientation:(UIDeviceOrientation)interfaceOrientation { + switch (interfaceOrientation) { + case UIDeviceOrientationFaceUp: + return AVCaptureVideoOrientationPortrait; + case UIDeviceOrientationFaceDown: + return AVCaptureVideoOrientationPortraitUpsideDown; + case UIDeviceOrientationLandscapeRight: + return AVCaptureVideoOrientationLandscapeLeft; + case UIDeviceOrientationLandscapeLeft: + return AVCaptureVideoOrientationLandscapeRight; + default: + return AVCaptureVideoOrientationPortrait; + } +} + #pragma mark - Session Configuration - (AVCaptureSession *)newSessionWithCaptureDevice:(AVCaptureDevice *)captureDevice { From fce726e33087029adb751cb4e76336e26d8e2681 Mon Sep 17 00:00:00 2001 From: Mike Buss Date: Sun, 17 Jul 2016 23:01:41 -0400 Subject: [PATCH 03/30] Fixes #71: Crash when result block is nil --- Classes/ios/Scanners/MTBBarcodeScanner.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Classes/ios/Scanners/MTBBarcodeScanner.m b/Classes/ios/Scanners/MTBBarcodeScanner.m index b5be448..24077f7 100644 --- a/Classes/ios/Scanners/MTBBarcodeScanner.m +++ b/Classes/ios/Scanners/MTBBarcodeScanner.m @@ -355,6 +355,8 @@ - (void)stopRecognizingTaps { - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection { + if (!self.resultBlock) return; + NSMutableArray *codes = [[NSMutableArray alloc] init]; for (AVMetadataObject *metaData in metadataObjects) { From bcf794a168a3c56f673523449c3b183c0bed2aeb Mon Sep 17 00:00:00 2001 From: Bradford Chessin Date: Sun, 14 Aug 2016 15:52:56 -0400 Subject: [PATCH 04/30] iOS 10 NSCameraUsageDescription Fix When testing the example project with iOS 10, you need to include NSCameraUsageDescription in the info plist otherwise the app will crash. --- Project/MTBBarcodeScanner/Info.plist | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Project/MTBBarcodeScanner/Info.plist b/Project/MTBBarcodeScanner/Info.plist index 7e7479f..8d8e3b4 100644 --- a/Project/MTBBarcodeScanner/Info.plist +++ b/Project/MTBBarcodeScanner/Info.plist @@ -22,5 +22,7 @@ $(CURRENT_PROJECT_VERSION) NSPrincipalClass + NSCameraUsageDescription + For testing purposes From 4d9e7d0e4f1ea392e1950a3a8605a98bfad91fe2 Mon Sep 17 00:00:00 2001 From: Mark Hudnall Date: Thu, 1 Sep 2016 15:30:53 -0700 Subject: [PATCH 05/30] Call startRunning on a background thread. Without this change, startRunning is called on the main thread, which blocks the UI. This change is consistent with Apple's sample application (https://developer.apple.com/library/ios/samplecode/AVCam/Introduction/Intro.html), which shows how to start and stop the capture session on another thread. --- Classes/ios/Scanners/MTBBarcodeScanner.m | 84 ++++++++++++++---------- 1 file changed, 49 insertions(+), 35 deletions(-) diff --git a/Classes/ios/Scanners/MTBBarcodeScanner.m b/Classes/ios/Scanners/MTBBarcodeScanner.m index 24077f7..bd6a5e2 100644 --- a/Classes/ios/Scanners/MTBBarcodeScanner.m +++ b/Classes/ios/Scanners/MTBBarcodeScanner.m @@ -26,6 +26,13 @@ @interface MTBBarcodeScanner () */ @property (nonatomic, strong) AVCaptureSession *session; +/*! + @property sessionQueue + @abstract + A queue used to process session actions in the background. + */ +@property (nonatomic) dispatch_queue_t sessionQueue; + /*! @property captureDevice @abstract @@ -152,6 +159,7 @@ - (instancetype)initWithPreviewView:(UIView *)previewView { _previewView = previewView; _metaDataObjectTypes = [self defaultMetaDataObjectTypes]; _allowTapToFocus = YES; + self.sessionQueue = dispatch_queue_create("sessionQueue", DISPATCH_QUEUE_SERIAL ); [self addRotationObserver]; } return self; @@ -171,6 +179,7 @@ - (instancetype)initWithMetadataObjectTypes:(NSArray *)metaDataObjectTypes previ _metaDataObjectTypes = metaDataObjectTypes; _previewView = previewView; _allowTapToFocus = YES; + self.sessionQueue = dispatch_queue_create("sessionQueue", DISPATCH_QUEUE_SERIAL ); [self addRotationObserver]; } return self; @@ -237,35 +246,45 @@ - (void)startScanningWithResultBlock:(void (^)(NSArray *codes))resultBlock { NSAssert(![MTBBarcodeScanner scanningIsProhibited], @"Scanning is prohibited on this device. \ Check requestCameraPermissionWithSuccess: method before calling startScanningWithResultBlock:"); NSAssert(resultBlock, @"startScanningWithResultBlock: requires a non-nil resultBlock."); - - // Configure the session - if (!self.hasExistingSession) { - self.captureDevice = [self newCaptureDeviceWithCamera:self.camera]; - self.session = [self newSessionWithCaptureDevice:self.captureDevice]; - self.hasExistingSession = YES; - } - - // Configure the rect of interest - self.captureOutput.rectOfInterest = [self rectOfInterestFromScanRect:self.scanRect]; - - // Configure the preview layer - self.capturePreviewLayer.cornerRadius = self.previewView.layer.cornerRadius; - [self.previewView.layer insertSublayer:self.capturePreviewLayer atIndex:0]; // Insert below all other views - [self refreshVideoOrientation]; - - // Configure 'tap to focus' functionality - [self configureTapToFocus]; - - self.resultBlock = resultBlock; - + // Start the session after all configurations - [self.session startRunning]; - - // Call that block now that we've started scanning - if (self.didStartScanningBlock) { - self.didStartScanningBlock(); - } - + dispatch_async(self.sessionQueue, ^{ + + // Configure the session + if (!self.hasExistingSession) { + self.captureDevice = [self newCaptureDeviceWithCamera:self.camera]; + self.session = [self newSessionWithCaptureDevice:self.captureDevice]; + self.hasExistingSession = YES; + } + + // Configure the rect of interest + self.captureOutput.rectOfInterest = [self rectOfInterestFromScanRect:self.scanRect]; + + self.resultBlock = resultBlock; + + [self.session startRunning]; + + dispatch_async(dispatch_get_main_queue(), ^{ + + // Configure the preview layer + self.capturePreviewLayer = nil; + self.capturePreviewLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.session]; + self.capturePreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; + self.capturePreviewLayer.cornerRadius = self.previewView.layer.cornerRadius; + self.capturePreviewLayer.frame = self.previewView.bounds; + [self refreshVideoOrientation]; + + // Configure 'tap to focus' functionality + [self configureTapToFocus]; + + [self.previewView.layer addSublayer:self.capturePreviewLayer]; // Insert below all other views + + // Call that block now that we've started scanning + if (self.didStartScanningBlock) { + self.didStartScanningBlock(); + } + }); + }); } - (void)stopScanning { @@ -281,7 +300,7 @@ - (void)stopScanning { // Stop recognizing taps for the 'Tap to Focus' feature [self stopRecognizingTaps]; - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + dispatch_async(self.sessionQueue, ^{ // When we're finished scanning, reset the settings for the camera // to their original states @@ -410,7 +429,7 @@ - (AVCaptureSession *)newSessionWithCaptureDevice:(AVCaptureDevice *)captureDevi [newSession setSessionPreset:AVCaptureSessionPresetHigh]; self.captureOutput = [[AVCaptureMetadataOutput alloc] init]; - [self.captureOutput setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()]; + [self.captureOutput setMetadataObjectsDelegate:self queue:self.sessionQueue]; [newSession addOutput:self.captureOutput]; self.captureOutput.metadataObjectTypes = self.metaDataObjectTypes; @@ -430,11 +449,6 @@ - (AVCaptureSession *)newSessionWithCaptureDevice:(AVCaptureDevice *)captureDevi self.captureOutput.rectOfInterest = [self rectOfInterestFromScanRect:self.scanRect]; - self.capturePreviewLayer = nil; - self.capturePreviewLayer = [AVCaptureVideoPreviewLayer layerWithSession:newSession]; - self.capturePreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill; - self.capturePreviewLayer.frame = self.previewView.bounds; - [newSession commitConfiguration]; return newSession; From b6478aebefe2599df6594390661ec1401b19dd6c Mon Sep 17 00:00:00 2001 From: John Goh Date: Mon, 24 Oct 2016 14:52:13 +0800 Subject: [PATCH 06/30] Hackathon Final --- Gemfile | 4 +- Gemfile.lock | 2 +- Project/MTBBarcodeScanner/Info.plist | 2 + .../project.pbxproj | 72 +++++- .../contents.xcworkspacedata | 10 + .../Base.lproj/LaunchScreen.xib | 12 +- .../Base.lproj/Main_iPad.storyboard | 73 +++--- .../Base.lproj/Main_iPhone.storyboard | 34 +-- .../ApplicationDelegate/MTBAppDelegate.m | 2 + .../MTBAdvancedExampleViewController.m | 208 +++++++++++++++++- .../GoogleService-Info.plist | 40 ++++ .../AppIcon.appiconset/Contents.json | 35 +++ .../MTBBarcodeScannerExample-Info.plist | 4 +- Project/Podfile | 14 ++ Project/Podfile.lock | 39 ++++ 15 files changed, 467 insertions(+), 84 deletions(-) create mode 100644 Project/MTBBarcodeScannerExample.xcworkspace/contents.xcworkspacedata create mode 100644 Project/MTBBarcodeScannerExample/GoogleService-Info.plist create mode 100644 Project/Podfile create mode 100644 Project/Podfile.lock diff --git a/Gemfile b/Gemfile index 96b0465..12223a5 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,3 @@ -source "https://rubygems.org" +source "http://rubygems.org" -gem 'cocoapods' \ No newline at end of file +gem 'cocoapods' diff --git a/Gemfile.lock b/Gemfile.lock index 640bf0d..e762cbd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,5 +1,5 @@ GEM - remote: https://rubygems.org/ + remote: http://rubygems.org/ specs: activesupport (4.2.1) i18n (~> 0.7) diff --git a/Project/MTBBarcodeScanner/Info.plist b/Project/MTBBarcodeScanner/Info.plist index 7e7479f..2fdc237 100644 --- a/Project/MTBBarcodeScanner/Info.plist +++ b/Project/MTBBarcodeScanner/Info.plist @@ -2,6 +2,8 @@ + NSCameraUsageDescription + Needed CFBundleDevelopmentRegion en CFBundleExecutable diff --git a/Project/MTBBarcodeScannerExample.xcodeproj/project.pbxproj b/Project/MTBBarcodeScannerExample.xcodeproj/project.pbxproj index 065b576..6440b38 100644 --- a/Project/MTBBarcodeScannerExample.xcodeproj/project.pbxproj +++ b/Project/MTBBarcodeScannerExample.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 0A2A60ADF2A282E55E33F865 /* libPods-MTBBarcodeScannerExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AD891B3FC407E0D1881DDD9C /* libPods-MTBBarcodeScannerExample.a */; }; 2705B18D1CDBE8D700E30BED /* MTBBarcode.h in Headers */ = {isa = PBXBuildFile; fileRef = 2705B18C1CDBE8D700E30BED /* MTBBarcode.h */; }; 2763D16B1A01432B0001EA05 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2763D1691A01432B0001EA05 /* LaunchScreen.xib */; }; 27C265E318A9B85E008E2A33 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27C265E218A9B85E008E2A33 /* QuartzCore.framework */; }; @@ -27,6 +28,7 @@ 563B26541CD9F96100DE7D8D /* MTBBarcodeScanner.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 563B264C1CD9F95F00DE7D8D /* MTBBarcodeScanner.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 563B26591CD9F98400DE7D8D /* MTBBarcodeScanner.h in Headers */ = {isa = PBXBuildFile; fileRef = 27C3BF8118A73FE700C44713 /* MTBBarcodeScanner.h */; settings = {ATTRIBUTES = (Public, ); }; }; 563B265A1CD9F98400DE7D8D /* MTBBarcodeScanner.m in Sources */ = {isa = PBXBuildFile; fileRef = 27C3BF8218A73FE700C44713 /* MTBBarcodeScanner.m */; }; + EF5544A91DBBB4770095FB93 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = EF5544A81DBBB4770095FB93 /* GoogleService-Info.plist */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -54,6 +56,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 249EC0A3432A4D2B3DA614CA /* Pods-MTBBarcodeScannerExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MTBBarcodeScannerExample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MTBBarcodeScannerExample/Pods-MTBBarcodeScannerExample.debug.xcconfig"; sourceTree = ""; }; 2705B18C1CDBE8D700E30BED /* MTBBarcode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTBBarcode.h; sourceTree = ""; }; 2763D16A1A01432B0001EA05 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 27B417E418A8A501004B70AE /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; @@ -83,6 +86,9 @@ 27C3BF8718A7475100C44713 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; 563B264C1CD9F95F00DE7D8D /* MTBBarcodeScanner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MTBBarcodeScanner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 563B26501CD9F96100DE7D8D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + AD891B3FC407E0D1881DDD9C /* libPods-MTBBarcodeScannerExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MTBBarcodeScannerExample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + EF5544A81DBBB4770095FB93 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + FD1C117B0C39709DBCD8F06F /* Pods-MTBBarcodeScannerExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MTBBarcodeScannerExample.release.xcconfig"; path = "Pods/Target Support Files/Pods-MTBBarcodeScannerExample/Pods-MTBBarcodeScannerExample.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -96,6 +102,7 @@ 27C3BF4418A7140F00C44713 /* CoreGraphics.framework in Frameworks */, 27C3BF4618A7140F00C44713 /* UIKit.framework in Frameworks */, 27C3BF4218A7140F00C44713 /* Foundation.framework in Frameworks */, + 0A2A60ADF2A282E55E33F865 /* libPods-MTBBarcodeScannerExample.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -133,6 +140,7 @@ 563B264D1CD9F96100DE7D8D /* MTBBarcodeScanner */, 27C3BF4018A7140F00C44713 /* Frameworks */, 27C3BF3F18A7140F00C44713 /* Products */, + 902C88ACF1F486FC7F1BDAD1 /* Pods */, ); sourceTree = ""; }; @@ -155,6 +163,7 @@ 27C3BF4318A7140F00C44713 /* CoreGraphics.framework */, 27C3BF4518A7140F00C44713 /* UIKit.framework */, 27C3BF6318A7140F00C44713 /* XCTest.framework */, + AD891B3FC407E0D1881DDD9C /* libPods-MTBBarcodeScannerExample.a */, ); name = Frameworks; sourceTree = ""; @@ -166,6 +175,7 @@ 27C3BF5318A7140F00C44713 /* Main_iPhone.storyboard */, 27C3BF5618A7140F00C44713 /* Main_iPad.storyboard */, 27C3BF5C18A7140F00C44713 /* Images.xcassets */, + EF5544A81DBBB4770095FB93 /* GoogleService-Info.plist */, 27C3BF4818A7140F00C44713 /* Supporting Files */, 2763D1691A01432B0001EA05 /* LaunchScreen.xib */, ); @@ -265,6 +275,15 @@ path = MTBBarcodeScanner; sourceTree = ""; }; + 902C88ACF1F486FC7F1BDAD1 /* Pods */ = { + isa = PBXGroup; + children = ( + 249EC0A3432A4D2B3DA614CA /* Pods-MTBBarcodeScannerExample.debug.xcconfig */, + FD1C117B0C39709DBCD8F06F /* Pods-MTBBarcodeScannerExample.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -284,10 +303,12 @@ isa = PBXNativeTarget; buildConfigurationList = 27C3BF7318A7140F00C44713 /* Build configuration list for PBXNativeTarget "MTBBarcodeScannerExample" */; buildPhases = ( + F79A31BC7FFC0FE123D8DDA2 /* Check Pods Manifest.lock */, 27C3BF3A18A7140F00C44713 /* Sources */, 27C3BF3B18A7140F00C44713 /* Frameworks */, 27C3BF3C18A7140F00C44713 /* Resources */, 563B26581CD9F96100DE7D8D /* Embed Frameworks */, + D03178C596BC1AEF2616C808 /* Copy Pods Resources */, ); buildRules = ( ); @@ -327,7 +348,8 @@ LastUpgradeCheck = 0720; TargetAttributes = { 27C3BF3D18A7140F00C44713 = { - DevelopmentTeam = K83F2KZFM9; + DevelopmentTeam = H5TNQ9W7QZ; + ProvisioningStyle = Automatic; }; 563B264B1CD9F95F00DE7D8D = { CreatedOnToolsVersion = 7.3; @@ -363,6 +385,7 @@ 27C3BF5D18A7140F00C44713 /* Images.xcassets in Resources */, 27C3BF5518A7140F00C44713 /* Main_iPhone.storyboard in Resources */, 27C3BF4C18A7140F00C44713 /* InfoPlist.strings in Resources */, + EF5544A91DBBB4770095FB93 /* GoogleService-Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -375,6 +398,39 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + D03178C596BC1AEF2616C808 /* Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-MTBBarcodeScannerExample/Pods-MTBBarcodeScannerExample-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + F79A31BC7FFC0FE123D8DDA2 /* Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 27C3BF3A18A7140F00C44713 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -525,18 +581,19 @@ }; 27C3BF7418A7140F00C44713 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 249EC0A3432A4D2B3DA614CA /* Pods-MTBBarcodeScannerExample.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEVELOPMENT_TEAM = K83F2KZFM9; + DEVELOPMENT_TEAM = H5TNQ9W7QZ; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "MTBBarcodeScannerExample/MTBBarcodeScannerExample-Prefix.pch"; INFOPLIST_FILE = "MTBBarcodeScannerExample/MTBBarcodeScannerExample-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.mikebuss.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_BUNDLE_IDENTIFIER = org.johngoh.MTBBarcodeScannerExample; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; WRAPPER_EXTENSION = app; @@ -545,18 +602,19 @@ }; 27C3BF7518A7140F00C44713 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = FD1C117B0C39709DBCD8F06F /* Pods-MTBBarcodeScannerExample.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEVELOPMENT_TEAM = K83F2KZFM9; + DEVELOPMENT_TEAM = H5TNQ9W7QZ; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "MTBBarcodeScannerExample/MTBBarcodeScannerExample-Prefix.pch"; INFOPLIST_FILE = "MTBBarcodeScannerExample/MTBBarcodeScannerExample-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = "com.mikebuss.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_BUNDLE_IDENTIFIER = org.johngoh.MTBBarcodeScannerExample; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; WRAPPER_EXTENSION = app; @@ -584,7 +642,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.mikebuss.MTBBarcodeScanner; + PRODUCT_BUNDLE_IDENTIFIER = org.johngoh.MTBBarcodeScanner; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -615,7 +673,7 @@ IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_BUNDLE_IDENTIFIER = com.mikebuss.MTBBarcodeScanner; + PRODUCT_BUNDLE_IDENTIFIER = org.johngoh.MTBBarcodeScanner; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; VERSIONING_SYSTEM = "apple-generic"; diff --git a/Project/MTBBarcodeScannerExample.xcworkspace/contents.xcworkspacedata b/Project/MTBBarcodeScannerExample.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..7f5f66f --- /dev/null +++ b/Project/MTBBarcodeScannerExample.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/Project/MTBBarcodeScannerExample/Base.lproj/LaunchScreen.xib b/Project/MTBBarcodeScannerExample/Base.lproj/LaunchScreen.xib index 9809ba4..ae22018 100644 --- a/Project/MTBBarcodeScannerExample/Base.lproj/LaunchScreen.xib +++ b/Project/MTBBarcodeScannerExample/Base.lproj/LaunchScreen.xib @@ -1,9 +1,10 @@ - + - + + @@ -13,7 +14,7 @@ - - + diff --git a/Project/MTBBarcodeScannerExample/Base.lproj/Main_iPad.storyboard b/Project/MTBBarcodeScannerExample/Base.lproj/Main_iPad.storyboard index 372039c..83bf7a3 100644 --- a/Project/MTBBarcodeScannerExample/Base.lproj/Main_iPad.storyboard +++ b/Project/MTBBarcodeScannerExample/Base.lproj/Main_iPad.storyboard @@ -1,12 +1,13 @@ - + - + + - + @@ -21,11 +22,11 @@ - + - @@ -76,6 +77,7 @@ + @@ -86,7 +88,7 @@ - + @@ -99,70 +101,64 @@ - - - + - + - - + @@ -178,7 +174,7 @@ - + @@ -203,28 +199,25 @@ - - + - - + @@ -233,11 +226,11 @@ - + - - + diff --git a/Project/MTBBarcodeScannerExample/Base.lproj/Main_iPhone.storyboard b/Project/MTBBarcodeScannerExample/Base.lproj/Main_iPhone.storyboard index ab6f088..7d001fb 100644 --- a/Project/MTBBarcodeScannerExample/Base.lproj/Main_iPhone.storyboard +++ b/Project/MTBBarcodeScannerExample/Base.lproj/Main_iPhone.storyboard @@ -1,6 +1,7 @@ - - + + + @@ -22,7 +23,7 @@ - + @@ -37,11 +38,11 @@ - + - @@ -126,11 +127,11 @@ - + - + @@ -278,7 +279,7 @@ - + @@ -293,4 +294,9 @@ + + + + + diff --git a/Project/MTBBarcodeScannerExample/Classes/ApplicationDelegate/MTBAppDelegate.m b/Project/MTBBarcodeScannerExample/Classes/ApplicationDelegate/MTBAppDelegate.m index b201b25..3649219 100644 --- a/Project/MTBBarcodeScannerExample/Classes/ApplicationDelegate/MTBAppDelegate.m +++ b/Project/MTBBarcodeScannerExample/Classes/ApplicationDelegate/MTBAppDelegate.m @@ -7,11 +7,13 @@ // #import "MTBAppDelegate.h" +#import "Firebase.h" @implementation MTBAppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. + [FIRApp configure]; return YES; } diff --git a/Project/MTBBarcodeScannerExample/Classes/Controllers/MTBAdvancedExampleViewController.m b/Project/MTBBarcodeScannerExample/Classes/Controllers/MTBAdvancedExampleViewController.m index e0f59f5..54aeb81 100644 --- a/Project/MTBBarcodeScannerExample/Classes/Controllers/MTBAdvancedExampleViewController.m +++ b/Project/MTBBarcodeScannerExample/Classes/Controllers/MTBAdvancedExampleViewController.m @@ -8,8 +8,18 @@ #import "MTBAdvancedExampleViewController.h" #import "MTBBarcodeScanner.h" +#import "Firebase.h" -@interface MTBAdvancedExampleViewController () +#define kTitle @"title" +#define kDescription @"description" +#define kColor @"color" +#define kDate @"date" +#define kExpiry @"expiry" + +@interface MTBAdvancedExampleViewController (){ + NSDate *lastSentTime; + NSMutableDictionary *products; +} @property (nonatomic, weak) IBOutlet UIView *previewView; @property (nonatomic, weak) IBOutlet UIButton *toggleScanningButton; @@ -19,6 +29,8 @@ @interface MTBAdvancedExampleViewController () @property (nonatomic, strong) MTBBarcodeScanner *scanner; @property (nonatomic, strong) NSMutableDictionary *overlayViews; @property (nonatomic, assign) BOOL didShowAlert; + +@property (strong, nonatomic) FIRDatabaseReference *ref; @end @implementation MTBAdvancedExampleViewController @@ -27,7 +39,76 @@ @implementation MTBAdvancedExampleViewController - (void)viewDidLoad { [super viewDidLoad]; + lastSentTime = [[NSDate alloc] init]; + + + products = [[NSMutableDictionary alloc] init]; + products[@"Sku101-1"] = @{ + kTitle:@"Samsung Note 7", + kDescription:@"512GB, 8GB Ram", + kDate:@"21 Oct 13", + kExpiry:@"21 Oct 16", + kColor:[UIColor redColor] + }; + products[@"Sku101-2"] = @{ + kTitle:@"Samsung Note 7", + kDescription:@"512GB, 8GB Ram", + kDate:@"21 Dec 13", + kExpiry:@"21 Dec 16", + kColor:[UIColor yellowColor] + }; + products[@"Sku101-3"] = @{ + kTitle:@"Samsung Note 7", + kDescription:@"512GB, 8GB Ram", + kDate:@"21 Dec 13", + kExpiry:@"21 Dec 16", + kColor:[UIColor yellowColor] + }; + products[@"Sku102-1"] = @{ + kTitle:@"Apple iPhone 7", + kDescription:@"128GB, 8GB Ram", + kDate:@"25 Jan 14", + kExpiry:@"25 Jan 17", + kColor:[UIColor yellowColor] + }; + products[@"Sku102-2"] = @{ + kTitle:@"Apple iPhone 7", + kDescription:@"128GB, 8GB Ram", + kDate:@"25 Jan 16", + kExpiry:@"25 Jan 19", + kColor:[UIColor greenColor] + }; + products[@"Sku102-3"] = @{ + kTitle:@"Apple iPhone 7", + kDescription:@"128GB, 8GB Ram", + kDate:@"25 Jan 16", + kExpiry:@"25 Jan 19", + kColor:[UIColor greenColor] + }; + products[@"Sku103-1"] = @{ + kTitle:@"Google Pixel", + kDescription:@"512GB, 16GB Ram", + kDate:@"22 Oct 16", + kExpiry:@"22 Oct 19", + kColor:[UIColor greenColor] + }; + products[@"Sku103-2"] = @{ + kTitle:@"Google Pixel", + kDescription:@"512GB, 16GB Ram", + kDate:@"9 Nov 15", + kExpiry:@"11 Sept 18", + kColor:[UIColor greenColor] + }; + products[@"Sku103-3"] = @{ + kTitle:@"Google Pixel", + kDescription:@"512GB, 16GB Ram", + kDate:@"9 Nov 15", + kExpiry:@"11 Sept 18", + kColor:[UIColor greenColor] + }; + + self.ref = [[FIRDatabase database] reference]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(deviceOrientationDidChange:) name:UIDeviceOrientationDidChangeNotification @@ -95,7 +176,10 @@ - (void)startScanning { } // Optionally set a rectangle of interest to scan codes. Only codes within this rect will be scanned. - self.scanner.scanRect = self.viewOfInterest.frame; + // self.scanner.scanRect = self.viewOfInterest.frame; + self.scanner.scanRect = self.previewView.frame; + [self.viewOfInterest setHidden:true]; + [self.toggleScanningButton setTitle:@"Stop Scanning" forState:UIControlStateNormal]; self.toggleScanningButton.backgroundColor = [UIColor redColor]; @@ -120,6 +204,7 @@ - (void)drawOverlaysOnCodes:(NSArray *)codes { } } + for (AVMetadataMachineReadableCodeObject *code in codes) { UIView *view = nil; NSString *codeString = code.stringValue; @@ -133,13 +218,11 @@ - (void)drawOverlaysOnCodes:(NSArray *)codes { view.frame = code.bounds; } else { - // First time seeing this code - BOOL isValidCode = [self isValidCodeString:codeString]; // Create an overlay UIView *overlayView = [self overlayForCodeString:codeString bounds:code.bounds - valid:isValidCode]; + corners:code.corners]; self.overlayViews[codeString] = overlayView; // Add the overlay to the preview view @@ -148,6 +231,40 @@ - (void)drawOverlaysOnCodes:(NSArray *)codes { } } } + + + + NSDate *now = [[NSDate alloc] init]; + NSDateFormatter *df = [[NSDateFormatter alloc] init]; + [df setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZ"]; + NSString *timeString = [df stringFromDate:now]; + + + NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; + for (NSString *str in codeStrings){ + NSString *model = [str substringToIndex:6]; + if ([model isEqualToString:@"Sku123"]|| + [model isEqualToString:@"Sku101"]|| + [model isEqualToString:@"Sku102"]|| + [model isEqualToString:@"Sku103"]){ + + if ([[dict allKeys] containsObject:model]) { + dict[model] = [[NSNumber alloc] initWithInt: [dict[model] intValue] + 1]; + }else{ + dict[model] = [[NSNumber alloc] initWithInt: 1]; + } + } + } + + + if ([lastSentTime timeIntervalSinceNow] < -2 || [[dict allKeys] count] < 1){ + if ([[dict allKeys] count] < 1){ + dict[@"Sku101"] = [[NSNumber alloc] initWithInt: 0]; + } + lastSentTime = now; + [[[_ref child:@"cctv"] child:timeString] setValue:dict]; + NSLog(@"%@",dict); + } } - (BOOL)isValidCodeString:(NSString *)codeString { @@ -155,22 +272,85 @@ - (BOOL)isValidCodeString:(NSString *)codeString { return stringIsValid; } -- (UIView *)overlayForCodeString:(NSString *)codeString bounds:(CGRect)bounds valid:(BOOL)valid { - UIColor *viewColor = valid ? [UIColor greenColor] : [UIColor redColor]; +- (UIView *)overlayForCodeString:(NSString *)codeString bounds:(CGRect)bounds corners:(NSArray *)corners { + // UIColor *viewColor = valid ? [UIColor greenColor] : [UIColor redColor]; + UIColor *viewColor = [UIColor whiteColor]; UIView *view = [[UIView alloc] initWithFrame:bounds]; UILabel *label = [[UILabel alloc] initWithFrame:view.bounds]; + // Configure the view - view.layer.borderWidth = 5.0; - view.backgroundColor = [viewColor colorWithAlphaComponent:0.75]; - view.layer.borderColor = viewColor.CGColor; + view.layer.borderWidth = 2.0; + view.backgroundColor = [viewColor colorWithAlphaComponent:0.5]; + view.layer.borderColor = [UIColor blackColor].CGColor; // Configure the label label.font = [UIFont boldSystemFontOfSize:12]; - label.text = codeString; label.textColor = [UIColor blackColor]; label.textAlignment = NSTextAlignmentCenter; label.numberOfLines = 0; + if (![[products allKeys] containsObject:codeString]){ + label.text = [@"No data found: " stringByAppendingString: codeString]; + }else{ + NSDictionary *product = products[codeString]; + UIColor *color = product[kColor]; + + view.backgroundColor = [color colorWithAlphaComponent:0.5]; + + label.text = codeString; + UIBezierPath *path = [UIBezierPath bezierPath]; + [path moveToPoint:CGPointMake(bounds.size.width, 0.0)]; + [path addLineToPoint:CGPointMake(bounds.size.width+50.0, -50.0)]; + CAShapeLayer *shapeLayer = [CAShapeLayer layer]; + shapeLayer.path = [path CGPath]; + shapeLayer.strokeColor = [[UIColor blackColor] CGColor]; + shapeLayer.lineWidth = 2.0; + shapeLayer.fillColor = [[UIColor clearColor] CGColor]; + [view.layer addSublayer:shapeLayer]; + + CGFloat width = 200.0; + UIView *card = [[UIView alloc] initWithFrame: CGRectMake(bounds.size.width+50.0, -50.0, width, 89.0)]; + card.layer.borderWidth = 2.0; + card.backgroundColor = [viewColor colorWithAlphaComponent:0.8]; + card.layer.borderColor = [UIColor blackColor].CGColor; + card.layer.zPosition = MAXFLOAT; + + UILabel *nameLabel = [[UILabel alloc] initWithFrame:CGRectMake(5,5, width-10.0, 20)]; + nameLabel.textColor = [UIColor blackColor]; + nameLabel.textAlignment = NSTextAlignmentLeft; + nameLabel.font = [UIFont boldSystemFontOfSize:15]; + nameLabel.text = product[kTitle]; + [card addSubview: nameLabel]; + UILabel *descLabel = [[UILabel alloc] initWithFrame:CGRectMake(5,27, width-10.0, 15)]; + descLabel.textColor = [UIColor blackColor]; + descLabel.textAlignment = NSTextAlignmentLeft; + descLabel.font = [UIFont italicSystemFontOfSize:12]; + descLabel.text = product[kDescription]; + [card addSubview: descLabel]; + UILabel *dateLabel = [[UILabel alloc] initWithFrame:CGRectMake(5,44, width-10.0, 18)]; + dateLabel.textColor = [UIColor blackColor]; + dateLabel.textAlignment = NSTextAlignmentLeft; + dateLabel.font = [UIFont systemFontOfSize:14]; + dateLabel.text = [@"MFG: " stringByAppendingString: product[kDate]]; + [card addSubview: dateLabel]; + UILabel *expLabel = [[UILabel alloc] initWithFrame:CGRectMake(5,64, width-10.0, 18)]; + expLabel.textColor = [UIColor blackColor]; + expLabel.textAlignment = NSTextAlignmentLeft; + expLabel.font = [UIFont systemFontOfSize:14]; + expLabel.text = [@"Expiry: " stringByAppendingString: product[kExpiry]]; + [card addSubview: expLabel]; + [view addSubview: card]; + + + } + + + +// for(NSDictionary* c in corners){ +// NSLog(@"%@",c); +// } + + // Add constraints to label to improve text size? @@ -189,6 +369,12 @@ - (void)stopScanning { for (NSString *code in self.overlayViews.allKeys) { [self.overlayViews[code] removeFromSuperview]; } + + NSDate *now = [[NSDate alloc] init]; + NSDateFormatter *df = [[NSDateFormatter alloc] init]; + [df setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZ"]; + NSString *timeString = [df stringFromDate:now]; + [[[_ref child:@"cctv"] child:timeString] setValue:@{@"Sku101":[[NSNumber alloc] initWithInt:0]}]; } #pragma mark - Actions diff --git a/Project/MTBBarcodeScannerExample/GoogleService-Info.plist b/Project/MTBBarcodeScannerExample/GoogleService-Info.plist new file mode 100644 index 0000000..9cadeb6 --- /dev/null +++ b/Project/MTBBarcodeScannerExample/GoogleService-Info.plist @@ -0,0 +1,40 @@ + + + + + AD_UNIT_ID_FOR_BANNER_TEST + ca-app-pub-3940256099942544/2934735716 + AD_UNIT_ID_FOR_INTERSTITIAL_TEST + ca-app-pub-3940256099942544/4411468910 + CLIENT_ID + 601254978524-f4htv63omob4v6vmed39tsuh666dsvna.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.601254978524-f4htv63omob4v6vmed39tsuh666dsvna + API_KEY + AIzaSyDTmyV5VbF3kDdTFGDRXuFoBM4eC91jwt0 + GCM_SENDER_ID + 601254978524 + PLIST_VERSION + 1 + BUNDLE_ID + org.johngoh.MTBBarcodeScannerExample + PROJECT_ID + overseer-c2931 + STORAGE_BUCKET + overseer-c2931.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:601254978524:ios:bac7442f08e84781 + DATABASE_URL + https://overseer-c2931.firebaseio.com + + \ No newline at end of file diff --git a/Project/MTBBarcodeScannerExample/Images.xcassets/AppIcon.appiconset/Contents.json b/Project/MTBBarcodeScannerExample/Images.xcassets/AppIcon.appiconset/Contents.json index b7f3352..1d060ed 100644 --- a/Project/MTBBarcodeScannerExample/Images.xcassets/AppIcon.appiconset/Contents.json +++ b/Project/MTBBarcodeScannerExample/Images.xcassets/AppIcon.appiconset/Contents.json @@ -1,15 +1,35 @@ { "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, { "idiom" : "iphone", "size" : "29x29", "scale" : "2x" }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, { "idiom" : "iphone", "size" : "40x40", "scale" : "2x" }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, { "idiom" : "iphone", "size" : "60x60", @@ -20,6 +40,16 @@ "size" : "60x60", "scale" : "3x" }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, { "idiom" : "ipad", "size" : "29x29", @@ -49,6 +79,11 @@ "idiom" : "ipad", "size" : "76x76", "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" } ], "info" : { diff --git a/Project/MTBBarcodeScannerExample/MTBBarcodeScannerExample-Info.plist b/Project/MTBBarcodeScannerExample/MTBBarcodeScannerExample-Info.plist index 5895551..0cf44b7 100644 --- a/Project/MTBBarcodeScannerExample/MTBBarcodeScannerExample-Info.plist +++ b/Project/MTBBarcodeScannerExample/MTBBarcodeScannerExample-Info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion en CFBundleDisplayName - ${PRODUCT_NAME} + Foresight CFBundleExecutable ${EXECUTABLE_NAME} CFBundleIdentifier @@ -21,7 +21,7 @@ CFBundleSignature ???? CFBundleVersion - 0 + 1 LSRequiresIPhoneOS NSCameraUsageDescription diff --git a/Project/Podfile b/Project/Podfile new file mode 100644 index 0000000..5b1c433 --- /dev/null +++ b/Project/Podfile @@ -0,0 +1,14 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '6.0' + +target 'MTBBarcodeScannerExample' do + +pod 'Firebase/Core' +pod 'Firebase/Database' + +end + +target 'MTBBarcodeScanner' do + +end + diff --git a/Project/Podfile.lock b/Project/Podfile.lock new file mode 100644 index 0000000..7693f6c --- /dev/null +++ b/Project/Podfile.lock @@ -0,0 +1,39 @@ +PODS: + - Firebase/Core (3.7.1): + - FirebaseAnalytics (= 3.4.4) + - FirebaseCore (= 3.4.3) + - Firebase/Database (3.7.1): + - Firebase/Core + - FirebaseDatabase (= 3.0.3) + - FirebaseAnalytics (3.4.4): + - FirebaseCore (~> 3.4) + - FirebaseInstanceID (~> 1.0) + - GoogleInterchangeUtilities (~> 1.2) + - GoogleSymbolUtilities (~> 1.1) + - FirebaseCore (3.4.3): + - GoogleInterchangeUtilities (~> 1.2) + - GoogleUtilities (~> 1.2) + - FirebaseDatabase (3.0.3): + - FirebaseAnalytics (~> 3.3) + - FirebaseInstanceID (1.0.8) + - GoogleInterchangeUtilities (1.2.2): + - GoogleSymbolUtilities (~> 1.1) + - GoogleSymbolUtilities (1.1.2) + - GoogleUtilities (1.3.2): + - GoogleSymbolUtilities (~> 1.1) + +DEPENDENCIES: + - Firebase/Core + - Firebase/Database + +SPEC CHECKSUMS: + Firebase: be473484f0d515e72ffd04acf22f981773c23e58 + FirebaseAnalytics: 0533a00b681e08fd0b2cd0444b7ddf0ef9fd7a1a + FirebaseCore: 5a885548bbc5f0c410b04f8e9ac9d73ff1221907 + FirebaseDatabase: 750a4a0fe18ef114318876fd654b2d9c671e0992 + FirebaseInstanceID: ba1e640935235e5fac39dfa816fe7660e72e1a8a + GoogleInterchangeUtilities: d5bc4d88d5b661ab72f9d70c58d02ca8c27ad1f7 + GoogleSymbolUtilities: 631ee17048aa5e9ab133470d768ea997a5ef9b96 + GoogleUtilities: 8bbc733218aad26306f9d4a253823986110e3358 + +COCOAPODS: 0.36.0 From f3b8daded61ff83386fe23a602323fc4e3c0d070 Mon Sep 17 00:00:00 2001 From: John Goh Date: Mon, 24 Oct 2016 15:13:44 +0800 Subject: [PATCH 07/30] Details Added --- README.md | 280 ++--------------------------------------------------- qrcode.pdf | Bin 0 -> 286257 bytes 2 files changed, 7 insertions(+), 273 deletions(-) create mode 100644 qrcode.pdf diff --git a/README.md b/README.md index 3f4fb66..eb8f08d 100644 --- a/README.md +++ b/README.md @@ -1,276 +1,10 @@ -# MTBBarcodeScanner +# Foresight +Winner of NTU ideasinc startathon Oct16 -[![Version](https://img.shields.io/cocoapods/v/MTBBarcodeScanner.svg?style=flat)](http://cocoadocs.org/docsets/MTBBarcodeScanner) -[![License](https://img.shields.io/cocoapods/l/MTBBarcodeScanner.svg?style=flat)](http://cocoadocs.org/docsets/MTBBarcodeScanner) -[![Platform](https://img.shields.io/cocoapods/p/MTBBarcodeScanner.svg?style=flat)](http://cocoadocs.org/docsets/MTBBarcodeScanner) -[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) +The idea for Foresight is a warehouse inventory system that uses existing CCTV feeds to that tracks where all items are. +Pairing the item location data with weight sensors would allow us to figure out what items are on the weight sensors. +And since each item has weight data, we can count the number of items, eliminating the need for manual inventory tracking/counting. -A lightweight, easy-to-use barcode scanning library for iOS 8+. This library is built on top of Apple's excellent AVFoundation framework, and will continue to receive updates as Apple releases them. +Qrcodes used are in qrcode.pdf -With this library you can: - -- Supply a custom UIView for displaying camera input -- Read any number of barcodes before stopping -- Read multiple codes on the screen at the same time (2D barcodes only) -- Easily read codes with a block, including the string value and position in the preview -- Easily flip from the back to the front camera -- Toggle the device's torch on and off -- Freeze and unfreeze capture to display a still image from the camera - -See demo project for examples of capturing one code, multiple codes, or highlighting codes as valid or invalid in the live preview. - ---- - - - -#### Sample Barcodes - - - ---- - -## Installation - -### CocoaPods - -MTBBarcodeScanner can be installed via [CocoaPods](http://cocoapods.org) by adding the following line to your Podfile: - -`pod "MTBBarcodeScanner"` - -### Carthage - -[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. - -You can install Carthage with [Homebrew](http://brew.sh/) using the following command: - -```bash -$ brew update -$ brew install carthage -``` - -To integrate MTBBarcodeScanner into your Xcode project using Carthage, specify it in your `Cartfile`: - -```ogdl -github "mikebuss/MTBBarcodeScanner" -``` - -Run `carthage update` to build the framework and drag the built `MTBBarcodeScanner.framework` into your Xcode project. - -### Manual - - -If you'd prefer not to use a dependency manager, you can download [these two files](https://github.com/mikebuss/MTBBarcodeScanner/tree/master/Classes/ios/Scanners) and add them to your project: - -[`MTBBarcodeScanner.h`](https://github.com/mikebuss/MTBBarcodeScanner/blob/master/Classes/ios/Scanners/MTBBarcodeScanner.h) - -[`MTBBarcodeScanner.m`](https://github.com/mikebuss/MTBBarcodeScanner/blob/master/Classes/ios/Scanners/MTBBarcodeScanner.m) - ---- - -## Usage - -To import the library: `#import "MTBBarcodeScanner.h"` - -#### Initialization - -To initialize an instance of `MTBBarcodeScanner`: - -```objective-c -scanner = [[MTBBarcodeScanner alloc] initWithPreviewView:self.previewView]; -``` - -Where `previewView` is the `UIView` in which the camera input will be displayed. - -If you only want to scan for certain MetaObjectTypes, you can initialize with the `initWithMetadataObjectTypes:previewView:` method: - -```objective-c -s = [[MTBBarcodeScanner alloc] initWithMetadataObjectTypes:@[AVMetadataObjectTypeQRCode] - previewView:self.previewView]; -``` - -#### iOS 10 and later - -If you are using the `MTBBarcodeScanner` library on iOS 10 and later, you need to include the following `Info.plist` key in order to request camera access or the application will crash: -```xml -NSCameraUsageDescription -Can we access your camera in order to scan barcodes? -``` -Of course you can also set your own (localized) message here. To find out more about privacy-keys in iOS, check the -[official Apple documentation](https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html). - -#### Scanning - -To read the first code and stop scanning: - -```objective-c -[MTBBarcodeScanner requestCameraPermissionWithSuccess:^(BOOL success) { - if (success) { - - NSError *error = nil; - [self.scanner startScanningWithResultBlock:^(NSArray *codes) { - AVMetadataMachineReadableCodeObject *code = [codes firstObject]; - NSLog(@"Found code: %@", code.stringValue); - - [self.scanner stopScanning]; - } error:&error]; - - } else { - // The user denied access to the camera - } -}]; -``` - -If the camera is pointed at more than one 2-dimensional code, you can read all of them: - -```objective-c -NSError *error = nil; -[self.scanner startScanningWithResultBlock:^(NSArray *codes) { - for (AVMetadataMachineReadableCodeObject *code in codes) { - NSLog(@"Found code: %@", code.stringValue); - } - [self.scanner stopScanning]; -} error:&error]; -``` - -**Note:** This only applies to 2-dimensional barcodes as 1-dimensional barcodes can only be read one at a time. See [relevant Apple document](https://developer.apple.com/library/ios/technotes/tn2325/_index.html). - -To continuously read and only output unique codes: - -```objective-c -NSError *error = nil; -[self.scanner startScanningWithResultBlock:^(NSArray *codes) { - for (AVMetadataMachineReadableCodeObject *code in codes) { - if ([self.uniqueCodes indexOfObject:code.stringValue] == NSNotFound) { - [self.uniqueCodes addObject:code.stringValue]; - NSLog(@"Found unique code: %@", code.stringValue); - } - } -} error:&error]; -``` - -#### Callback Blocks - -An alternative way to setup MTBBarcodeScanner is to configure the blocks directly, like so: - -```objective-c -self.scanner.didStartScanningBlock = ^{ - NSLog(@"The scanner started scanning! We can now hide any activity spinners."); -}; - -self.scanner.resultBlock = ^(NSArray *codes){ - NSLog(@"Found these codes: %@", codes); -}; - -self.scanner.didTapToFocusBlock = ^(CGPoint point){ - NSLog(@"The user tapped the screen to focus. \ - Here we could present a view at %@", NSStringFromCGPoint(point)); -}; - -[self.scanner startScanning]; -``` - -This is useful if you would like to present a spinner while MTBBarcodeScanner is initializing. - -If you would like to reference `self` in one of these blocks, remember to use a weak reference to avoid a retain cycle: - -```objective-c -__weak MyViewController *weakSelf = self; -self.scanner.resultBlock = ^(NSArray *codes){ - [weakSelf drawOverlaysOnCodes:codes]; -}; -``` - ---- - -## Tap to Focus - -By default, MTBBarcodeScanner will allow the user to tap the screen to focus the camera. To disable this functionality, set the `allowTapToFocus` property to NO. To be notified when the user taps the screen, provide a block for the `didTapToFocusBlock` property, like so: - -```objective-c -self.scanner.didTapToFocusBlock = ^(CGPoint point){ - NSLog(@"The user tapped the screen to focus. \ - Here we could present a view at %@", NSStringFromCGPoint(point)); -}; -``` - ---- - -## Switching Cameras - -Switch to the opposite camera with the `flipCamera` method on the scanner: - -```objective-c - -- (IBAction)switchCameraTapped:(id)sender { - [self.scanner flipCamera]; -} - -``` - - -Or specify the camera directly using the `camera` property, like so: - -```objective-c - -MTBBarcodeScanner *scanner = [[MTBBarcodeScanner alloc] initWithPreviewView:_previewView]; -scanner.camera = MTBCameraFront; - -``` - -Examples for these are in the demo project. - ---- - -## Freezing Capture - -Under some circumstances you may want to freeze the video feed when capturing barcodes. To do this, call the `freezeCapture` and `unfreezeCapture` methods. - ---- - -## Limiting the Scan Zone - -To limit the section of the screen that barcodes can be scanned in, set the `scanRect` property on MTBBarcodeScanner. See `MTBAdvancedExampleViewController` for an example of this. - ---- - -## Controlling the Torch - -To control the torch, set the `torchMode` property or call the `toggleTorch` method. - -Available values include: - -```objective-c -MTBTorchModeOff, -MTBTorchModeOn, -MTBTorchModeAuto -``` - ---- - -## Capturing Still Images - -To capture a still image, call the `captureStillImage:` method after you've started scanning. - ---- - -## Design Considerations - -The primary goals of this library are to: - -- Provide an easy-to-use interface for barcode scanning -- Make as few assumptions about the scanning process as possible - - Don't assume the user wants to scan one code at a time - - Don't assume the camera input view should be a particular size - - Don't assume the scanning process will have it's own view controller - -## Developer - -Mike Buss -- [Website](http://mikebuss.com) -- [GitHub](https://github.com/mikebuss) -- [Twitter](https://twitter.com/michaeltbuss) -- [Email](mailto:mike@mikebuss.com) - -## License - -MTBBarcodeScanner is available under the MIT license. See the LICENSE file for more info. +This is the "CCTV" (iOS app) used in the hackathon. \ No newline at end of file diff --git a/qrcode.pdf b/qrcode.pdf new file mode 100644 index 0000000000000000000000000000000000000000..52b5bdf7af357c134e88aa26be2d10b7fee932f1 GIT binary patch literal 286257 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
    2~5_G0?_orPo>f6{)KM=_X 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 literal 0 HcmV?d00001 From 070646db298d9635803093c26b9d04dc32bf74e0 Mon Sep 17 00:00:00 2001 From: PiotrPawlus Date: Fri, 2 Jun 2017 13:05:46 +0200 Subject: [PATCH 08/30] Unlock main thread - delete inputs and outputs in this same queue. --- Classes/ios/Scanners/MTBBarcodeScanner.m | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Classes/ios/Scanners/MTBBarcodeScanner.m b/Classes/ios/Scanners/MTBBarcodeScanner.m index 7d7980d..73edeb7 100644 --- a/Classes/ios/Scanners/MTBBarcodeScanner.m +++ b/Classes/ios/Scanners/MTBBarcodeScanner.m @@ -312,6 +312,9 @@ - (void)stopScanning { return; } + AVCaptureSession *session = self.session; + self.session = nil; + // Turn the torch off self.torchMode = MTBTorchModeOff; @@ -323,21 +326,22 @@ - (void)stopScanning { // When we're finished scanning, reset the settings for the camera // to their original states - [self removeDeviceInput]; - - for (AVCaptureOutput *output in self.session.outputs) { - [self.session removeOutput:output]; - } self.resultBlock = nil; self.capturePreviewLayer = nil; - AVCaptureSession *session = self.session; - self.session = nil; - dispatch_async(self.privateSessionQueue, ^{ // Must be dispatched as it is blocking + [session stopRunning]; + + for (AVCaptureInput *input in session.inputs) { + [session removeInput:input]; + } + + for (AVCaptureOutput *output in session.outputs) { + [session removeOutput:output]; + } }); } From 3024f84950faffbc559a8d13fcaae003cee8455d Mon Sep 17 00:00:00 2001 From: Melendez Xavier Date: Wed, 28 Feb 2018 10:36:07 +0100 Subject: [PATCH 09/30] Configure ISO and ExposureTime to normal values if camera is used elsewhere with different settings --- Classes/ios/Scanners/MTBBarcodeScanner.m | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/Classes/ios/Scanners/MTBBarcodeScanner.m b/Classes/ios/Scanners/MTBBarcodeScanner.m index 6303583..906357e 100644 --- a/Classes/ios/Scanners/MTBBarcodeScanner.m +++ b/Classes/ios/Scanners/MTBBarcodeScanner.m @@ -147,6 +147,22 @@ @interface MTBBarcodeScanner () *)metaDataObjec // Library does not support scanning for faces NSAssert(!([metaDataObjectTypes indexOfObject:AVMetadataObjectTypeFace] != NSNotFound), @"The type %@ is not supported by MTBBarcodeScanner.", AVMetadataObjectTypeFace); - + _exposureTime = CMTimeMake(1, 30); + _iso = 100; _metaDataObjectTypes = metaDataObjectTypes; _previewView = previewView; _allowTapToFocus = YES; @@ -589,6 +606,11 @@ - (AVCaptureDevice *)newCaptureDeviceWithCamera:(MTBCamera)camera { if ([newCaptureDevice isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) { newCaptureDevice.focusMode = AVCaptureFocusModeContinuousAutoFocus; } + if (!newCaptureDevice.isAdjustingExposure) + [newCaptureDevice + setExposureModeCustomWithDuration:self.exposureTime + ISO:self.iso + completionHandler:nil]; [newCaptureDevice unlockForConfiguration]; } else { NSLog(@"Failed to acquire lock for initial focus mode: %@", error); From 03b7c0e6a3af2466fbc63fd7c94d5f7de57d2a09 Mon Sep 17 00:00:00 2001 From: Guillaume Rager Date: Sat, 18 Apr 2020 17:41:46 +0200 Subject: [PATCH 10/30] Update project.pbxproj --- .../MTBBarcodeScannerExample.xcodeproj/project.pbxproj | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Project/MTBBarcodeScannerExample.xcodeproj/project.pbxproj b/Project/MTBBarcodeScannerExample.xcodeproj/project.pbxproj index 92bc8fb..31f3aa5 100644 --- a/Project/MTBBarcodeScannerExample.xcodeproj/project.pbxproj +++ b/Project/MTBBarcodeScannerExample.xcodeproj/project.pbxproj @@ -332,7 +332,7 @@ LastUpgradeCheck = 1010; TargetAttributes = { 27C3BF3D18A7140F00C44713 = { - DevelopmentTeam = K83F2KZFM9; + DevelopmentTeam = MUDN37DV26; LastSwiftMigration = 1010; }; 563B264B1CD9F95F00DE7D8D = { @@ -570,13 +570,13 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEVELOPMENT_TEAM = K83F2KZFM9; + DEVELOPMENT_TEAM = MUDN37DV26; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "MTBBarcodeScannerExample/MTBBarcodeScannerExample-Prefix.pch"; INFOPLIST_FILE = "MTBBarcodeScannerExample/MTBBarcodeScannerExample-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.mikebuss.MTBBarcodeScannerExample2; + PRODUCT_BUNDLE_IDENTIFIER = "com.prg-consulting.MTBBarcodeScannerExample2"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; SWIFT_OBJC_BRIDGING_HEADER = "MTBBarcodeScannerExample/MTBBarcodeScannerExample-Bridging-Header.h"; @@ -595,13 +595,13 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEVELOPMENT_TEAM = K83F2KZFM9; + DEVELOPMENT_TEAM = MUDN37DV26; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "MTBBarcodeScannerExample/MTBBarcodeScannerExample-Prefix.pch"; INFOPLIST_FILE = "MTBBarcodeScannerExample/MTBBarcodeScannerExample-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.mikebuss.MTBBarcodeScannerExample2; + PRODUCT_BUNDLE_IDENTIFIER = "com.prg-consulting.MTBBarcodeScannerExample2"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; SWIFT_OBJC_BRIDGING_HEADER = "MTBBarcodeScannerExample/MTBBarcodeScannerExample-Bridging-Header.h"; From db9f1f86fc8d678daaf817f9a743dd2885ebb570 Mon Sep 17 00:00:00 2001 From: yunxiwangluo Date: Tue, 21 Apr 2020 16:31:13 +0800 Subject: [PATCH 11/30] =?UTF-8?q?=E5=B0=86AVCaptureDevice=E4=BB=8E.m?= =?UTF-8?q?=E7=A7=BB=E5=88=B0.h?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Classes/ios/Scanners/MTBBarcodeScanner.h | 7 +++++++ Classes/ios/Scanners/MTBBarcodeScanner.m | 12 ++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Classes/ios/Scanners/MTBBarcodeScanner.h b/Classes/ios/Scanners/MTBBarcodeScanner.h index 616802e..dc25d26 100644 --- a/Classes/ios/Scanners/MTBBarcodeScanner.h +++ b/Classes/ios/Scanners/MTBBarcodeScanner.h @@ -29,6 +29,13 @@ typedef NS_ENUM(NSUInteger, MTBTorchMode) { @interface MTBBarcodeScanner : NSObject +/*! + @property captureDevice + @abstract + Represents the physical device that is used for scanning barcodes. + */ +@property (nonatomic, strong) AVCaptureDevice *captureDevice; + /** * The currently set camera. See MTBCamera for options. * diff --git a/Classes/ios/Scanners/MTBBarcodeScanner.m b/Classes/ios/Scanners/MTBBarcodeScanner.m index 8885b2c..218429f 100644 --- a/Classes/ios/Scanners/MTBBarcodeScanner.m +++ b/Classes/ios/Scanners/MTBBarcodeScanner.m @@ -38,12 +38,12 @@ @interface MTBBarcodeScanner () Date: Mon, 27 Jul 2020 21:56:58 +1000 Subject: [PATCH 12/30] Initial commit for swift 5 conversion Convert MTBBarcodeScanner to from ObjC to Swift 5 Separate errors into custom swift errors Update method names to be compatible with swift conventions Update method signatures to be throwing instead of passing NSError pointers Convert all callbacks to delegate pattern Update podspec file to include subspecs for ObjC and Swift, defaulted to ObjC --- .../ios/Scanners/MTBBarcodeScanner.h | 0 .../ios/Scanners/MTBBarcodeScanner.m | 0 Classes/{ => ObjC}/osx/.gitkeep | 0 Classes/Swift/ios/MTBBarcodeScanner.swift | 890 ++++++++++++++++++ .../Swift/ios/MTBBarcodeScannerDelegate.swift | 43 + .../Swift/ios/MTBBarcodeScannerError.swift | 95 ++ Classes/Swift/osx/.gitkeep | 0 MTBBarcodeScanner.podspec | 17 +- .../{ => ObjC}/MTBBarcodeScanner/Info.plist | 0 .../{ => ObjC}/MTBBarcodeScanner/MTBBarcode.h | 0 .../project.pbxproj | 4 +- .../contents.xcworkspacedata | 0 .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../xcschemes/MTBBarcodeScanner.xcscheme | 0 .../Base.lproj/LaunchScreen.xib | 0 .../Base.lproj/Main_iPad.storyboard | 0 .../Base.lproj/Main_iPhone.storyboard | 0 .../ApplicationDelegate/MTBAppDelegate.h | 0 .../ApplicationDelegate/MTBAppDelegate.m | 0 .../MTBAdvancedExampleViewController.h | 0 .../MTBAdvancedExampleViewController.m | 0 .../MTBBasicExampleViewController.h | 0 .../MTBBasicExampleViewController.m | 0 .../SwiftExampleViewController.swift | 0 .../AppIcon.appiconset/Contents.json | 0 .../LaunchImage.launchimage/Contents.json | 0 .../flip.imageset/Contents.json | 0 .../Images.xcassets/flip.imageset/flip.png | Bin .../Images.xcassets/flip.imageset/flip@2x.png | Bin .../reverse.imageset/Contents.json | 0 .../reverse.imageset/reverse.png | Bin .../reverse.imageset/reverse@2x.png | Bin ...MTBBarcodeScannerExample-Bridging-Header.h | 0 .../MTBBarcodeScannerExample-Info.plist | 0 .../MTBBarcodeScannerExample-Prefix.pch | 0 .../en.lproj/InfoPlist.strings | 0 .../MTBBarcodeScannerExample/main.m | 0 .../MTBBarcodeScannerExampleTests-Info.plist | 0 .../en.lproj/InfoPlist.strings | 0 39 files changed, 1044 insertions(+), 5 deletions(-) rename Classes/{ => ObjC}/ios/Scanners/MTBBarcodeScanner.h (100%) rename Classes/{ => ObjC}/ios/Scanners/MTBBarcodeScanner.m (100%) rename Classes/{ => ObjC}/osx/.gitkeep (100%) create mode 100644 Classes/Swift/ios/MTBBarcodeScanner.swift create mode 100644 Classes/Swift/ios/MTBBarcodeScannerDelegate.swift create mode 100644 Classes/Swift/ios/MTBBarcodeScannerError.swift create mode 100644 Classes/Swift/osx/.gitkeep rename Project/{ => ObjC}/MTBBarcodeScanner/Info.plist (100%) rename Project/{ => ObjC}/MTBBarcodeScanner/MTBBarcode.h (100%) rename Project/{ => ObjC}/MTBBarcodeScannerExample.xcodeproj/project.pbxproj (99%) rename Project/{ => ObjC}/MTBBarcodeScannerExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata (100%) rename Project/{ => ObjC}/MTBBarcodeScannerExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename Project/{ => ObjC}/MTBBarcodeScannerExample.xcodeproj/xcshareddata/xcschemes/MTBBarcodeScanner.xcscheme (100%) rename Project/{ => ObjC}/MTBBarcodeScannerExample/Base.lproj/LaunchScreen.xib (100%) rename Project/{ => ObjC}/MTBBarcodeScannerExample/Base.lproj/Main_iPad.storyboard (100%) rename Project/{ => ObjC}/MTBBarcodeScannerExample/Base.lproj/Main_iPhone.storyboard (100%) rename Project/{ => ObjC}/MTBBarcodeScannerExample/Classes/ApplicationDelegate/MTBAppDelegate.h (100%) rename Project/{ => ObjC}/MTBBarcodeScannerExample/Classes/ApplicationDelegate/MTBAppDelegate.m (100%) rename Project/{ => ObjC}/MTBBarcodeScannerExample/Classes/Controllers/MTBAdvancedExampleViewController.h (100%) rename Project/{ => ObjC}/MTBBarcodeScannerExample/Classes/Controllers/MTBAdvancedExampleViewController.m (100%) rename Project/{ => ObjC}/MTBBarcodeScannerExample/Classes/Controllers/MTBBasicExampleViewController.h (100%) rename Project/{ => ObjC}/MTBBarcodeScannerExample/Classes/Controllers/MTBBasicExampleViewController.m (100%) rename Project/{ => ObjC}/MTBBarcodeScannerExample/Classes/Controllers/SwiftExampleViewController.swift (100%) rename Project/{ => ObjC}/MTBBarcodeScannerExample/Images.xcassets/AppIcon.appiconset/Contents.json (100%) rename Project/{ => ObjC}/MTBBarcodeScannerExample/Images.xcassets/LaunchImage.launchimage/Contents.json (100%) rename Project/{ => ObjC}/MTBBarcodeScannerExample/Images.xcassets/flip.imageset/Contents.json (100%) rename Project/{ => ObjC}/MTBBarcodeScannerExample/Images.xcassets/flip.imageset/flip.png (100%) rename Project/{ => ObjC}/MTBBarcodeScannerExample/Images.xcassets/flip.imageset/flip@2x.png (100%) rename Project/{ => ObjC}/MTBBarcodeScannerExample/Images.xcassets/reverse.imageset/Contents.json (100%) rename Project/{ => ObjC}/MTBBarcodeScannerExample/Images.xcassets/reverse.imageset/reverse.png (100%) rename Project/{ => ObjC}/MTBBarcodeScannerExample/Images.xcassets/reverse.imageset/reverse@2x.png (100%) rename Project/{ => ObjC}/MTBBarcodeScannerExample/MTBBarcodeScannerExample-Bridging-Header.h (100%) rename Project/{ => ObjC}/MTBBarcodeScannerExample/MTBBarcodeScannerExample-Info.plist (100%) rename Project/{ => ObjC}/MTBBarcodeScannerExample/MTBBarcodeScannerExample-Prefix.pch (100%) rename Project/{ => ObjC}/MTBBarcodeScannerExample/en.lproj/InfoPlist.strings (100%) rename Project/{ => ObjC}/MTBBarcodeScannerExample/main.m (100%) rename Project/{ => ObjC}/MTBBarcodeScannerExampleTests/MTBBarcodeScannerExampleTests-Info.plist (100%) rename Project/{ => ObjC}/MTBBarcodeScannerExampleTests/en.lproj/InfoPlist.strings (100%) diff --git a/Classes/ios/Scanners/MTBBarcodeScanner.h b/Classes/ObjC/ios/Scanners/MTBBarcodeScanner.h similarity index 100% rename from Classes/ios/Scanners/MTBBarcodeScanner.h rename to Classes/ObjC/ios/Scanners/MTBBarcodeScanner.h diff --git a/Classes/ios/Scanners/MTBBarcodeScanner.m b/Classes/ObjC/ios/Scanners/MTBBarcodeScanner.m similarity index 100% rename from Classes/ios/Scanners/MTBBarcodeScanner.m rename to Classes/ObjC/ios/Scanners/MTBBarcodeScanner.m diff --git a/Classes/osx/.gitkeep b/Classes/ObjC/osx/.gitkeep similarity index 100% rename from Classes/osx/.gitkeep rename to Classes/ObjC/osx/.gitkeep diff --git a/Classes/Swift/ios/MTBBarcodeScanner.swift b/Classes/Swift/ios/MTBBarcodeScanner.swift new file mode 100644 index 0000000..ba8b0ad --- /dev/null +++ b/Classes/Swift/ios/MTBBarcodeScanner.swift @@ -0,0 +1,890 @@ +// +// MTBBarcodeScanner.swift +// +// Created by Sam Mortazavi on 21/7/20. +// + +import UIKit + + +public enum MTBCamera { + case back + case front + + fileprivate var avPosition: AVCaptureDevice.Position { + switch self { + case .back: + return .back + case .front: + return .front + } + } +} + +/** + * Available torch modes when scanning barcodes. + * + * While AVFoundation provides an additional automatic + * mode, it is not supported here because it only works + * with video recordings, not barcode scanning. + */ +public enum MTBTorchMode { + case off + case on + + fileprivate var avTorchMode: AVCaptureDevice.TorchMode { + switch self { + case .off: + return .off + case .on: + return .on + } + } +} + + +public class MTBBarcodeScanner: NSObject { + + /// Starting or stopping the capture session should only be done on this queue. + private let privateSessionQueue: DispatchQueue + + /// The capture session used for scanning barcodes. + private var session: AVCaptureSession! + + /// Represents the physical device that is used for scanning barcodes. + private var captureDevice: AVCaptureDevice! + + /// The layer used to view the camera input. This layer is added to the + /// previewView when scanning starts. + private var capturePreviewLayer: AVCaptureVideoPreviewLayer! + + /// The current capture device input for capturing video. This is used + /// to reset the camera to its initial properties when scanning stops. + private var currentCaptureDeviceInput: AVCaptureDeviceInput! + + /// The capture device output for capturing video. + private var captureOutput: AVCaptureMetadataOutput! + + /// The MetaDataObjectTypes to look for in the scanning session. + /// + /// Only objects with a AVMetadataObject.ObjectType found in this set will be + /// reported to the result block. + private var metaDataObjectTypes: Set + + /// The view used to preview the camera input. + /// + /// The AVCaptureVideoPreviewLayer is added to this view to preview the + /// camera input when scanning starts. When scanning stops, the layer is + /// removed. + private let previewView: UIView + + /// The auto focus range restriction the AVCaptureDevice was initially configured for when scanning started. + /// + /// When startScanning is called, the auto focus range restriction of the default AVCaptureDevice + /// is stored. When stopScanning is called, the AVCaptureDevice is reset to the initial range restriction + /// to prevent a bug in the AVFoundation framework. + private var initialAutoFocusRangeRestriction: AVCaptureDevice.AutoFocusRangeRestriction! + + /// The focus point the AVCaptureDevice was initially configured for when scanning started. + /// + /// When startScanning is called, the focus point of the default AVCaptureDevice + /// is stored. When stopScanning is called, the AVCaptureDevice is reset to the initial focal point + /// to prevent a bug in the AVFoundation framework. + private var initialFocusPoint: CGPoint! + + /// Used for still image capture prior to iOS 10 + private var stillImageOutput: AVCaptureStillImageOutput! + + /// If allowTapToFocus is set to YES, this gesture recognizer is added to the `previewView` + /// when scanning starts. When the user taps the view, the `focusPointOfInterest` will change + /// to the location the user tapped. + private var gestureRecognizer: UITapGestureRecognizer! + + /// The currently set camera. See MTBCamera for options. + /// + /// Use [setCamera(_:)](x-source-tag://setCamera) to set or change the camera. + private(set) var camera: MTBCamera? + + /// Control the torch on the device, if present. + /// + /// Attempting to set the torch mode to an unsupported state + /// will fail silently, and the value passed into the setter + /// will be discarded. + /// + /// see setTorchMode + public var torchMode: MTBTorchMode? + + /// Allow the user to tap the previewView to focus a specific area. + /// Defaults to YES. + public var allowTapToFocus = false + + /// If set, only barcodes inside this area will be scanned. + /// + /// Setting this property is only supported while the scanner is active. + /// Use the didStartScanningBlock if you want to set it as early as + /// possible. + public var scanRect = CGRect.zero + + /// Layer used to present the camera input. If the previewView + /// does not use auto layout, it may be necessary to adjust the layers frame. + public var previewLayer: CALayer? + + /// Auto focus range restriction, if supported. + /// + /// Defaults to AVCaptureAutoFocusRangeRestrictionNear. Will be ignored on unsupported devices. + private var preferredAutoFocusRangeRestriction: AVCaptureDevice.AutoFocusRangeRestriction! + + + @available(iOS 10.0, *) + private lazy var output: AVCapturePhotoOutput = { AVCapturePhotoOutput() + }() + + + public weak var delegate: MTBBarcodeScannerDelegate? = nil + + + // MARK: - Default Values + + public static var defaultMetaDataObjectTypes: Set { + return [.qr, + .upce, + .code39, + .code39Mod43, + .ean13, + .ean8, + .code93, + .code128, + .pdf417, + .aztec, + .interleaved2of5, + .itf14, + .dataMatrix + ] + + } + + public let focalPointOfInterestX: CGFloat = 0.5 + public let focalPointOfInterestY: CGFloat = 0.5 + + + private override init() { + fatalError("MTBBarcodeScanner init is not supported. Please use init(previewView:metadataObjectTypes:) to instantiate a MTBBarcodeScanner") + } + + + public init(previewView: UIView, metaDataObjectTypes: Set = defaultMetaDataObjectTypes) { + + if metaDataObjectTypes.isEmpty { + // If the metaDataObjectTypes list is empty the default types will be used. + self.metaDataObjectTypes = MTBBarcodeScanner.defaultMetaDataObjectTypes + } else { + // Any type other than the ones in defaultMetaDataObjectTypes (like `.face`, `.humanBody`, etc.) will be ignored because they are not supported by MTBBarcideScanner. + self.metaDataObjectTypes = metaDataObjectTypes.intersection(MTBBarcodeScanner.defaultMetaDataObjectTypes) + + } + + self.previewView = previewView + self.allowTapToFocus = true + self.preferredAutoFocusRangeRestriction = .near + self.privateSessionQueue = DispatchQueue(label: "com.mikebuss.MTBBarcodeScanner.captureSession") + + super.init() + + self.addObservers() + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + + + // MARK: - Scanning + + public class var isCameraPresent: Bool { + // capture device is nil if status is AVAuthorizationStatusRestricted + return AVCaptureDevice.default(for: .video) != nil + } + + public class func hasCamera(camera: MTBCamera) -> Bool { + let position: AVCaptureDevice.Position = camera.avPosition + + if #available(iOS 10.0, *) { + + let device: AVCaptureDevice? = AVCaptureDevice.default(.builtInWideAngleCamera, + for: .video, + position: position) + return device != nil + } else { + // Array is empty if status is AVAuthorizationStatusRestricted + for device in AVCaptureDevice.devices(for: .video) { + if device.position == position { + return true + } + } + } + return false + } + + public class func oppositeCameraOf(camera: MTBCamera) -> MTBCamera { + switch (camera) { + case .back: + return .front + + case .front: + return .back + } + } + + public class var isScanningProhibited: Bool { + switch (AVCaptureDevice.authorizationStatus(for: .video)) { + case .denied, .restricted: + return true + + default: + return false + } + } + + public class func requestCameraPermission(successBlock: @escaping (Bool)->Void) { + if !self.isCameraPresent { + successBlock(false) + return + } + + switch (AVCaptureDevice.authorizationStatus(for: .video)) { + + case .notDetermined: + AVCaptureDevice.requestAccess(for: .video, + completionHandler: { granted in + DispatchQueue.main.async { + successBlock(granted) + } + }) + case .authorized: + successBlock(true) + + case .denied, .restricted: + successBlock(false) + + @unknown default: + // Unknown status, Defaulting to denied status. + successBlock(false) + } + + } + + + public func startScanning(withCamera camera: MTBCamera = .back) throws -> Void { + if !MTBBarcodeScanner.isCameraPresent { + throw MTBBarcodeScannerError.ScannerStartError.cameraNotPresent + } + if MTBBarcodeScanner.isScanningProhibited { + throw MTBBarcodeScannerError.ScannerStartError.scanningProhibited + } + + if (self.session != nil) { + throw MTBBarcodeScannerError.ScannerStartError.sessionAlreadyActive + } + + // Configure the session + self.camera = camera + self.captureDevice = self.newCaptureDevice(withCamera: camera) + let session: AVCaptureSession = try self.newSession(with: self.captureDevice) + + self.session = session + + // Configure the preview layer + self.capturePreviewLayer.cornerRadius = self.previewView.layer.cornerRadius + self.previewView.layer.insertSublayer(self.capturePreviewLayer, at:0) // Insert below all other views + self.refreshVideoOrientation() + + // Configure 'tap to focus' functionality + self.configureTapToFocus() + + privateSessionQueue.async { [weak self] in + guard let self = self else { return } + // Configure the rect of interest + self.captureOutput.rectOfInterest = self.rectOfInterest(from: self.scanRect) + + // Start the session after all configurations: + // Must be dispatched as it is blocking + self.session.startRunning() + if !self.scanRect.isEmpty { + self.captureOutput.rectOfInterest = self.capturePreviewLayer.metadataOutputRectConverted(fromLayerRect: self.scanRect) + } + // Alert the delegate now that we've started scanning. + // Dispatch back to main + DispatchQueue.main.async { + self.delegate?.barcodeScannerDidStartScanning() + } + } + } + + public func stopScanning() { + if (self.session == nil) { + return + } + + // Turn the torch off + self.torchMode = .off + + // Remove the preview layer + self.capturePreviewLayer.removeFromSuperlayer() + + // Stop recognizing taps for the 'Tap to Focus' feature + self.stopRecognizingTaps() + + // self.resultBlock = nil + self.capturePreviewLayer.session = nil + self.capturePreviewLayer = nil + + let session:AVCaptureSession! = self.session + let deviceInput: AVCaptureDeviceInput? = self.currentCaptureDeviceInput + self.session = nil + + privateSessionQueue.async { [weak self] in + guard let self = self else { return } + // When we're finished scanning, reset the settings for the camera + // to their original states + // Must be dispatched as it is blocking + + // Remove the device input if it was set + if let deviceInput = deviceInput { + self.removeDeviceInput(deviceInput, from: session) + } + for output: AVCaptureOutput in session.outputs { + session.removeOutput(output) + } + + // Must be dispatched as it is blocking + session.stopRunning() + } + } + + public var isScanning: Bool { + return self.session.isRunning + } + + public var hasOppositeCamera: Bool { + guard let camera = camera else { return false } + let otherCamera = MTBBarcodeScanner.oppositeCameraOf(camera: camera) + return MTBBarcodeScanner.hasCamera(camera: otherCamera) + } + + + public func flipCamera() throws { + guard let camera = self.camera else { + throw MTBBarcodeScannerError.CameraFlipError.cameraNotSet + } + if !self.isScanning { + throw MTBBarcodeScannerError.CameraFlipError.notScanning + } + + let otherCamera:MTBCamera = MTBBarcodeScanner.oppositeCameraOf(camera: camera) + try self.setCamera(otherCamera) + } + + + // MARK: - Tap to Focus + + private func configureTapToFocus() { + if self.allowTapToFocus { + let tapGesture = UITapGestureRecognizer(target:self, action:#selector(focusTapped(_:))) + self.previewView.addGestureRecognizer(tapGesture) + self.gestureRecognizer = tapGesture + } + } + + @objc private func focusTapped(_ tapGesture: UITapGestureRecognizer) { + let tapPoint = self.gestureRecognizer.location(in: self.gestureRecognizer.view) + let devicePoint = self.capturePreviewLayer.captureDevicePointConverted(fromLayerPoint: tapPoint) + + guard let device = self.captureDevice else { + NSLog("Capture device has not been set") + return + } + + do { + try device.lockForConfiguration() + if device.isFocusPointOfInterestSupported, device.isFocusModeSupported(.continuousAutoFocus) { + device.focusPointOfInterest = devicePoint + device.focusMode = .continuousAutoFocus + } + device.unlockForConfiguration() + } catch { + NSLog("Failed to acquire lock for focus change: \(error)") + } + + self.delegate?.barcodeScanner(didTapToFocusOn: tapPoint) + } + + private func stopRecognizingTaps() { + gestureRecognizer.isEnabled = false + if (self.gestureRecognizer != nil) { + self.previewView.removeGestureRecognizer(self.gestureRecognizer) + } + } + + + // MARK: - Rotation + + @objc private func handleApplicationDidChangeStatusBarNotification(_ notification:NSNotification) { + self.refreshVideoOrientation() + } + + private func refreshVideoOrientation() { + let orientation = UIApplication.shared.statusBarOrientation + self.capturePreviewLayer.frame = self.previewView.bounds + if let connection = self.capturePreviewLayer.connection, connection.isVideoOrientationSupported { + connection.videoOrientation = self.captureOrientation(for: orientation) + } + } + + private func captureOrientation(for interfaceOrientation: UIInterfaceOrientation) -> AVCaptureVideoOrientation { + switch (interfaceOrientation) { + case .portrait: + return .portrait + case .portraitUpsideDown: + return .portraitUpsideDown + case .landscapeLeft: + return .landscapeLeft + case .landscapeRight: + return .landscapeRight + default: + return .portrait + } + } + + + // MARK: - Background Handling + + @objc private func applicationWillEnterForegroundNotification(_ notification:NSNotification) { + // the torch is switched off when the app is backgrounded so we restore the + // previous state once the app is foregrounded again + if let torchMode = torchMode { + try? self.updateForTorchMode(preferredTorchMode: torchMode) + } + } + + + // MARK: - Session Configuration + + private func newSession(with captureDevice:AVCaptureDevice!) throws -> AVCaptureSession! { + let input:AVCaptureDeviceInput + do { + input = try AVCaptureDeviceInput.init(device: captureDevice) + } catch { + throw error + } + + let newSession = AVCaptureSession() + self.setDeviceInput(input, for: newSession) + + // Set an optimized preset for barcode scanning + newSession.sessionPreset = AVCaptureSession.Preset.high + + self.captureOutput = AVCaptureMetadataOutput() + self.captureOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main) + + newSession.addOutput(self.captureOutput) + self.captureOutput.metadataObjectTypes = Array(self.metaDataObjectTypes) + + newSession.beginConfiguration() + + if #available(iOS 10.0, *) { + self.output = AVCapturePhotoOutput() + self.output.isHighResolutionCaptureEnabled = true + + if newSession.canAddOutput(self.output) { + newSession.addOutput(self.output) + } + } else { + // Still image capture configuration + self.stillImageOutput = AVCaptureStillImageOutput() + self.stillImageOutput.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG] + + if self.stillImageOutput.isStillImageStabilizationSupported { + self.stillImageOutput.automaticallyEnablesStillImageStabilizationWhenAvailable = true + } + + self.stillImageOutput.isHighResolutionStillImageOutputEnabled = true + newSession.addOutput(self.stillImageOutput) + } + + self.privateSessionQueue.async { [weak self] in + guard let self = self else { return } + self.captureOutput.rectOfInterest = self.rectOfInterest(from: self.scanRect) + } + + self.capturePreviewLayer = AVCaptureVideoPreviewLayer(session: newSession) + self.capturePreviewLayer.videoGravity = .resizeAspectFill + self.capturePreviewLayer.frame = self.previewView.bounds + + newSession.commitConfiguration() + + return newSession + } + + private func newCaptureDevice(withCamera camera:MTBCamera) -> AVCaptureDevice? { + var newCaptureDevice:AVCaptureDevice? + let position:AVCaptureDevice.Position = camera.avPosition + + if #available(iOS 10.0, *) { + let device: AVCaptureDevice? = AVCaptureDevice.default(.builtInWideAngleCamera, + for: .video, + position: position) + newCaptureDevice = device + } else { + let videoDevices = AVCaptureDevice.devices(for: .video) + for device in videoDevices { + if device.position == position { + newCaptureDevice = device + break + } + } + } + + // If the front camera is not available, use the back camera + if (newCaptureDevice == nil) { + newCaptureDevice = AVCaptureDevice.default(for: .video) + } + + guard let captureDevice = newCaptureDevice else { + return nil + } + + + do { + + try captureDevice.lockForConfiguration() + // Using AVCaptureFocusModeContinuousAutoFocus helps improve scan times + if captureDevice.isFocusModeSupported(.continuousAutoFocus) { + captureDevice.focusMode = .continuousAutoFocus + } + captureDevice.unlockForConfiguration() + } catch { + NSLog("Failed to acquire lock for initial focus mode: \(error)") + } + + return newCaptureDevice + } + + class func devicePosition(for camera: MTBCamera) -> AVCaptureDevice.Position { + return camera.avPosition + } + + + // MARK: - Helper Methods + + private func addObservers() { + NotificationCenter.default.addObserver(self, + selector: #selector(handleApplicationDidChangeStatusBarNotification(_:)), + name: UIApplication.didChangeStatusBarOrientationNotification, + object: nil) + + NotificationCenter.default.addObserver(self, + selector: #selector(applicationWillEnterForegroundNotification(_:)), + name: UIApplication.willEnterForegroundNotification, + object: nil) + } + + private func setDeviceInput(_ deviceInput: AVCaptureDeviceInput, for session: AVCaptureSession) { + + self.removeDeviceInput(self.currentCaptureDeviceInput, from: session) + + self.currentCaptureDeviceInput = deviceInput + self.updateFocusPreferencesOfDevice(deviceInput.device, reset: false) + session.addInput(deviceInput) + } + + private func removeDeviceInput(_ deviceInput: AVCaptureDeviceInput, from session: AVCaptureSession) { + + // Restore focus settings to the previously saved state + self.updateFocusPreferencesOfDevice(deviceInput.device, reset: true) + + session.removeInput(deviceInput) + self.currentCaptureDeviceInput = nil + } + + private func updateFocusPreferencesOfDevice(_ inputDevice: AVCaptureDevice, reset: Bool) { + + do { + try inputDevice.lockForConfiguration() + } catch { + NSLog("Failed to acquire lock to (re)set focus options: \(error)") + return + } + + // Prioritize the focus on objects near to the device + if inputDevice.isAutoFocusRangeRestrictionSupported { + if !reset { + self.initialAutoFocusRangeRestriction = inputDevice.autoFocusRangeRestriction + inputDevice.autoFocusRangeRestriction = self.preferredAutoFocusRangeRestriction + } else { + inputDevice.autoFocusRangeRestriction = self.initialAutoFocusRangeRestriction + } + } + + // Focus on the center of the image + if inputDevice.isFocusPointOfInterestSupported { + if !reset { + self.initialFocusPoint = inputDevice.focusPointOfInterest + inputDevice.focusPointOfInterest = CGPoint(x: focalPointOfInterestX, y: focalPointOfInterestY) + } else { + inputDevice.focusPointOfInterest = self.initialFocusPoint + } + } + + inputDevice.unlockForConfiguration() + + // this method will acquire its own lock + if let torchMode = torchMode { + try? self.updateForTorchMode(preferredTorchMode: torchMode) + } + } + + + // MARK: - Torch Control + + public func setTorchMode(torchMode: MTBTorchMode) throws { + try self.updateForTorchMode(preferredTorchMode: torchMode) + } + + public func toggleTorch() { + switch (self.torchMode) { + case .on: + self.torchMode = .off + + case .off: + self.torchMode = .on + + case .none: + break + } + } + + private func updateForTorchMode(preferredTorchMode: MTBTorchMode) throws { + let avTorchMode = preferredTorchMode.avTorchMode + guard let backCamera = AVCaptureDevice.default(for: .video), backCamera.isTorchAvailable, backCamera.isTorchModeSupported(avTorchMode) else { + throw MTBBarcodeScannerError.torchModeUnavailable + } + + do { + try backCamera.lockForConfiguration() + } catch { + NSLog("Failed to acquire lock to update torch mode.") + throw error + } + + backCamera.torchMode = avTorchMode + backCamera.unlockForConfiguration() + } + + public func hasTorch() -> Bool { + guard let captureDevice = self.newCaptureDevice(withCamera: .back) else { return false } + return captureDevice.hasTorch + } + + + // MARK: - Capture + + public func freezeCapture() { + // we must access the layer on the main thread, but manipulating + // the capture connection is blocking and should be dispatched + guard let session = session, let connection = self.capturePreviewLayer.connection else { + return + } + + self.privateSessionQueue.async { + connection.isEnabled = false + session.stopRunning() + } + } + + public func unfreezeCapture() { + guard let session = session, let connection = self.capturePreviewLayer.connection else { + return + } + + if !session.isRunning { + self.setDeviceInput(self.currentCaptureDeviceInput, for: session) + + self.privateSessionQueue.async { [weak self] in + guard let self = self else { return } + session.startRunning() + connection.isEnabled = true + + DispatchQueue.main.async { + self.delegate?.barcodeScannerDidUnfreezScanner() + } + } + } + } + + + public func captureStillImage() throws { + if self.isCapturingStillImage() { + throw MTBBarcodeScannerError.StillImageCaptureError.captureInProgress + } + + if #available(iOS 10.0, *) { + let settings = AVCapturePhotoSettings() + settings.isAutoStillImageStabilizationEnabled = false + settings.flashMode = .off + settings.isHighResolutionPhotoEnabled = true + + self.privateSessionQueue.async { [weak self] in + guard let self = self else { return } + self.output.capturePhoto(with: settings, delegate: self) + + } + } else { + + guard let stillConnection = self.stillImageOutput.connection(with: .video) else { + throw MTBBarcodeScannerError.StillImageCaptureError.sessionIsClosed + } + + self.stillImageOutput.captureStillImageAsynchronously(from: stillConnection) { [weak self] imageDataSampleBuffer, error in + guard let self = self else { return } + + self.processCapturedStillImage(sampleBuffer: imageDataSampleBuffer, error: error) + } + + } + } + + private func processCapturedStillImage(sampleBuffer: CMSampleBuffer?, error: Error?) { + guard let sampleBuffer = sampleBuffer, error == nil else { + DispatchQueue.main.async { + self.delegate?.barcodeScanner(failedToCaptureStillImageWith: error ?? MTBBarcodeScannerError.StillImageCaptureError.imageCreationFailed) + } + return + } + + guard let jpegData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer), + let image = UIImage(data: jpegData) else { + DispatchQueue.main.async { + self.delegate?.barcodeScanner(failedToCaptureStillImageWith: MTBBarcodeScannerError.StillImageCaptureError.imageCreationFailed) + } + return + } + + DispatchQueue.main.async { + self.delegate?.barcodeScanner(didCapture: image) + } + } + + + private func isCapturingStillImage() -> Bool { + return self.stillImageOutput.isCapturingStillImage + } + + + // MARK: - Setters + + /// - Tag: setCamera + public func setCamera(_ camera: MTBCamera) throws { + if self.camera == camera { + return + } + + if !self.isScanning { + throw MTBBarcodeScannerError.CameraSetError.sessionNotRunning + } + + guard let captureDevice = self.newCaptureDevice(withCamera: camera) else { + throw MTBBarcodeScannerError.CameraSetError.noCaptureDeviceAvailable + } + let input = try AVCaptureDeviceInput(device: captureDevice) + + self.setDeviceInput(input, for:self.session) + self.camera = camera + + } + + public func setScanRect(scanRect: CGRect) { + + if !self.isScanning { + return + } + + self.refreshVideoOrientation() + + self.scanRect = scanRect + + // Only set now if scanning. Otherwise will be set in after scanning starts + if !self.isScanning { + return + } + + self.privateSessionQueue.async { [weak self] in + guard let self = self else { return } + self.captureOutput.rectOfInterest = self.capturePreviewLayer.metadataOutputRectConverted(fromLayerRect: scanRect) + } + + } + + public func setPreferredAutoFocusRangeRestriction(preferredAutoFocusRangeRestriction: AVCaptureDevice.AutoFocusRangeRestriction) { + if self.preferredAutoFocusRangeRestriction == preferredAutoFocusRangeRestriction { + return + } + + self.preferredAutoFocusRangeRestriction = preferredAutoFocusRangeRestriction + + if (self.currentCaptureDeviceInput == nil) { + // the setting will be picked up once a new session incl. device input is created + return + } + + self.updateFocusPreferencesOfDevice(self.currentCaptureDeviceInput.device, reset: false) + } + + + // MARK: - Helper Methods + + public func rectOfInterest(from scanRect: CGRect) -> CGRect { + let rect: CGRect + if !self.scanRect.isEmpty { + rect = self.capturePreviewLayer.metadataOutputRectConverted(fromLayerRect: self.scanRect) + } else { + rect = CGRect(x: 0, y: 0, width: 1, height: 1) // Default rectOfInterest for AVCaptureMetadataOutput + } + return rect + } +} + + +// MARK: - AVCaptureMetadataOutputObjects Delegate + +extension MTBBarcodeScanner: AVCaptureMetadataOutputObjectsDelegate { + + public func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) { + let codes = metadataObjects.compactMap { self.capturePreviewLayer.transformedMetadataObject(for: $0) as? AVMetadataMachineReadableCodeObject } + + self.delegate?.barcodeScanner(didRecognize: codes) + } + +} + + +// MARK: - AVCapturePhotoCaptureDelegate + +extension MTBBarcodeScanner: AVCapturePhotoCaptureDelegate { + + @available(iOS 11.0, *) + public func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) { + guard error == nil, + let data = photo.fileDataRepresentation(), + let image = UIImage(data: data) else { + delegate?.barcodeScanner(failedToCaptureStillImageWith: error ?? MTBBarcodeScannerError.StillImageCaptureError.imageCreationFailed) + return + } + delegate?.barcodeScanner(didCapture: image) + } + + @available(iOS 10.0, *) + public func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photoSampleBuffer: CMSampleBuffer?, previewPhoto previewPhotoSampleBuffer: CMSampleBuffer?, resolvedSettings: AVCaptureResolvedPhotoSettings, bracketSettings: AVCaptureBracketedStillImageSettings?, error: Error?) { + self.processCapturedStillImage(sampleBuffer: photoSampleBuffer, error: error) + } + +} diff --git a/Classes/Swift/ios/MTBBarcodeScannerDelegate.swift b/Classes/Swift/ios/MTBBarcodeScannerDelegate.swift new file mode 100644 index 0000000..11ec680 --- /dev/null +++ b/Classes/Swift/ios/MTBBarcodeScannerDelegate.swift @@ -0,0 +1,43 @@ +// +// MTBBarcodeScannerDelegate.swift +// +// Created by Sam Mortazavi on 21/7/20. +// + +import UIKit + + +public protocol MTBBarcodeScannerDelegate: class { + + func barcodeScannerDidStartScanning() + + func barcodeScannerDidUnfreezScanner() + + func barcodeScanner(didTapToFocustAt point: CGPoint) + + func barcodeScanner(didRecognize barcodes: [AVMetadataMachineReadableCodeObject]) + + func barcodeScanner(failedToCaptureStillImageWith error: Error) + + func barcodeScanner(didCapture stillImage: UIImage) + + func barcodeScanner(didTapToFocusOn point: CGPoint) +} + +extension MTBBarcodeScannerDelegate { + + func barcodeScannerDidStartScanning() {} + + func barcodeScannerDidUnfreezScanner() {} + + func barcodeScanner(didTapToFocustAt point: CGPoint) {} + + func barcodeScanner(failedToCaptureStillImageWith error: Error) {} + + func barcodeScanner(didCapture stillImage: UIImage) {} + + func barcodeScanner(didTapToFocusOn point: CGPoint) {} +} + + + diff --git a/Classes/Swift/ios/MTBBarcodeScannerError.swift b/Classes/Swift/ios/MTBBarcodeScannerError.swift new file mode 100644 index 0000000..7e0bfe6 --- /dev/null +++ b/Classes/Swift/ios/MTBBarcodeScannerError.swift @@ -0,0 +1,95 @@ +// +// MTBBarcodeScannerError.swift +// +// Created by Sam Mortazavi on 21/7/20. +// + +public enum MTBBarcodeScannerError: Error { + + public enum ScannerStartError: Error { + case cameraNotPresent + case scanningProhibited + case sessionAlreadyActive + } + + public enum StillImageCaptureError: Error { + case captureInProgress + case imageCreationFailed + case sessionIsClosed + + } + + public enum CameraSetError: Error { + case sessionNotRunning + case noCaptureDeviceAvailable + } + + public enum CameraFlipError: Error { + case notScanning + case cameraNotSet + } + + case torchModeUnavailable +} + + +extension MTBBarcodeScannerError.ScannerStartError: LocalizedError { + public var errorDescription: String? { + switch self { + case .cameraNotPresent: + return "Could not start scanner because the device doesn't have a camera.\nCheck 'requestCameraPermission' method before calling 'startScanning'" + case .scanningProhibited: + return "Could not start scanner because scanning is prohibited on this device.\nCheck 'requestCameraPermission' method before calling 'startScanning'" + case .sessionAlreadyActive: + return "Another session is in already in use." + } + } +} + + +extension MTBBarcodeScannerError.StillImageCaptureError: LocalizedError { + public var errorDescription: String? { + switch self { + case .captureInProgress: + return "Still image capture is already in progress. Check with isCapturingStillImage" + case .imageCreationFailed: + return "Failed to create a still image." + case .sessionIsClosed: + return "AVCaptureConnection is closed." + } + } +} + + +extension MTBBarcodeScannerError: LocalizedError { + public var errorDescription: String? { + switch self { + case .torchModeUnavailable: + return "Torch unavailable or mode not supported." + } + } +} + + +extension MTBBarcodeScannerError.CameraSetError: LocalizedError { + public var errorDescription: String? { + switch self { + case .sessionNotRunning: + return "Camera cannot be set when session is not running." + case .noCaptureDeviceAvailable: + return "Was not able to create any suitable capture device." + } + } +} + + +extension MTBBarcodeScannerError.CameraFlipError: LocalizedError { + public var errorDescription: String? { + switch self { + case .notScanning: + return "Camera can't be flipped when barcode scanner is not scanning" + case .cameraNotSet: + return "Camera can't be flipped because it has not been set yet" + } + } +} diff --git a/Classes/Swift/osx/.gitkeep b/Classes/Swift/osx/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/MTBBarcodeScanner.podspec b/MTBBarcodeScanner.podspec index c57f46d..882e03a 100644 --- a/MTBBarcodeScanner.podspec +++ b/MTBBarcodeScanner.podspec @@ -5,12 +5,23 @@ Pod::Spec.new do |s| s.homepage = "https://github.com/mikebuss/MTBBarcodeScanner" s.license = 'MIT' s.author = { "Mike Buss" => "mike@mikebuss.com" } - s.source = { :git => "https://github.com/mikebuss/MTBBarcodeScanner.git", :tag => s.version.to_s } + s.source = { :git => "https://github.com/SwiftySam/MTBBarcodeScanner.git", :tag => s.version.to_s } s.platform = :ios, '8.0' s.ios.deployment_target = '8.0' s.requires_arc = true - - s.source_files = 'Classes/ios/**/*.{h,m}' s.frameworks = 'AVFoundation', 'QuartzCore' + + s.default_subspec = 'ObjC' + s.swift_versions = ['5.1','5.2'] + + s.subspec 'ObjC' do |ss| + ss.ios.deployment_target = '8.0' + ss.source_files = 'Classes/Objc/ios/**/*.{h,m}' + end + + s.subspec 'Swift' do |ss| + ss.ios.deployment_target = '8.0' + ss.source_files = 'Classes/Swift/ios/**/*.{swift}' + end end diff --git a/Project/MTBBarcodeScanner/Info.plist b/Project/ObjC/MTBBarcodeScanner/Info.plist similarity index 100% rename from Project/MTBBarcodeScanner/Info.plist rename to Project/ObjC/MTBBarcodeScanner/Info.plist diff --git a/Project/MTBBarcodeScanner/MTBBarcode.h b/Project/ObjC/MTBBarcodeScanner/MTBBarcode.h similarity index 100% rename from Project/MTBBarcodeScanner/MTBBarcode.h rename to Project/ObjC/MTBBarcodeScanner/MTBBarcode.h diff --git a/Project/MTBBarcodeScannerExample.xcodeproj/project.pbxproj b/Project/ObjC/MTBBarcodeScannerExample.xcodeproj/project.pbxproj similarity index 99% rename from Project/MTBBarcodeScannerExample.xcodeproj/project.pbxproj rename to Project/ObjC/MTBBarcodeScannerExample.xcodeproj/project.pbxproj index 92bc8fb..0ecfc83 100644 --- a/Project/MTBBarcodeScannerExample.xcodeproj/project.pbxproj +++ b/Project/ObjC/MTBBarcodeScannerExample.xcodeproj/project.pbxproj @@ -79,8 +79,8 @@ 27C3BF6318A7140F00C44713 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 27C3BF6B18A7140F00C44713 /* MTBBarcodeScannerExampleTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "MTBBarcodeScannerExampleTests-Info.plist"; sourceTree = ""; }; 27C3BF6D18A7140F00C44713 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; - 27C3BF8118A73FE700C44713 /* MTBBarcodeScanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MTBBarcodeScanner.h; path = ../../Classes/ios/Scanners/MTBBarcodeScanner.h; sourceTree = ""; }; - 27C3BF8218A73FE700C44713 /* MTBBarcodeScanner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MTBBarcodeScanner.m; path = ../../Classes/ios/Scanners/MTBBarcodeScanner.m; sourceTree = ""; }; + 27C3BF8118A73FE700C44713 /* MTBBarcodeScanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MTBBarcodeScanner.h; path = ../../../Classes/ObjC/ios/Scanners/MTBBarcodeScanner.h; sourceTree = ""; }; + 27C3BF8218A73FE700C44713 /* MTBBarcodeScanner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MTBBarcodeScanner.m; path = ../../../Classes/ObjC/ios/Scanners/MTBBarcodeScanner.m; sourceTree = ""; }; 27C3BF8718A7475100C44713 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; 27F0CD251DD3853500640B69 /* MTBBarcodeScannerExample-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MTBBarcodeScannerExample-Bridging-Header.h"; sourceTree = ""; }; 27F0CD261DD3853600640B69 /* SwiftExampleViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwiftExampleViewController.swift; path = Classes/Controllers/SwiftExampleViewController.swift; sourceTree = ""; }; diff --git a/Project/MTBBarcodeScannerExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Project/ObjC/MTBBarcodeScannerExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from Project/MTBBarcodeScannerExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to Project/ObjC/MTBBarcodeScannerExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/Project/MTBBarcodeScannerExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Project/ObjC/MTBBarcodeScannerExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from Project/MTBBarcodeScannerExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to Project/ObjC/MTBBarcodeScannerExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/Project/MTBBarcodeScannerExample.xcodeproj/xcshareddata/xcschemes/MTBBarcodeScanner.xcscheme b/Project/ObjC/MTBBarcodeScannerExample.xcodeproj/xcshareddata/xcschemes/MTBBarcodeScanner.xcscheme similarity index 100% rename from Project/MTBBarcodeScannerExample.xcodeproj/xcshareddata/xcschemes/MTBBarcodeScanner.xcscheme rename to Project/ObjC/MTBBarcodeScannerExample.xcodeproj/xcshareddata/xcschemes/MTBBarcodeScanner.xcscheme diff --git a/Project/MTBBarcodeScannerExample/Base.lproj/LaunchScreen.xib b/Project/ObjC/MTBBarcodeScannerExample/Base.lproj/LaunchScreen.xib similarity index 100% rename from Project/MTBBarcodeScannerExample/Base.lproj/LaunchScreen.xib rename to Project/ObjC/MTBBarcodeScannerExample/Base.lproj/LaunchScreen.xib diff --git a/Project/MTBBarcodeScannerExample/Base.lproj/Main_iPad.storyboard b/Project/ObjC/MTBBarcodeScannerExample/Base.lproj/Main_iPad.storyboard similarity index 100% rename from Project/MTBBarcodeScannerExample/Base.lproj/Main_iPad.storyboard rename to Project/ObjC/MTBBarcodeScannerExample/Base.lproj/Main_iPad.storyboard diff --git a/Project/MTBBarcodeScannerExample/Base.lproj/Main_iPhone.storyboard b/Project/ObjC/MTBBarcodeScannerExample/Base.lproj/Main_iPhone.storyboard similarity index 100% rename from Project/MTBBarcodeScannerExample/Base.lproj/Main_iPhone.storyboard rename to Project/ObjC/MTBBarcodeScannerExample/Base.lproj/Main_iPhone.storyboard diff --git a/Project/MTBBarcodeScannerExample/Classes/ApplicationDelegate/MTBAppDelegate.h b/Project/ObjC/MTBBarcodeScannerExample/Classes/ApplicationDelegate/MTBAppDelegate.h similarity index 100% rename from Project/MTBBarcodeScannerExample/Classes/ApplicationDelegate/MTBAppDelegate.h rename to Project/ObjC/MTBBarcodeScannerExample/Classes/ApplicationDelegate/MTBAppDelegate.h diff --git a/Project/MTBBarcodeScannerExample/Classes/ApplicationDelegate/MTBAppDelegate.m b/Project/ObjC/MTBBarcodeScannerExample/Classes/ApplicationDelegate/MTBAppDelegate.m similarity index 100% rename from Project/MTBBarcodeScannerExample/Classes/ApplicationDelegate/MTBAppDelegate.m rename to Project/ObjC/MTBBarcodeScannerExample/Classes/ApplicationDelegate/MTBAppDelegate.m diff --git a/Project/MTBBarcodeScannerExample/Classes/Controllers/MTBAdvancedExampleViewController.h b/Project/ObjC/MTBBarcodeScannerExample/Classes/Controllers/MTBAdvancedExampleViewController.h similarity index 100% rename from Project/MTBBarcodeScannerExample/Classes/Controllers/MTBAdvancedExampleViewController.h rename to Project/ObjC/MTBBarcodeScannerExample/Classes/Controllers/MTBAdvancedExampleViewController.h diff --git a/Project/MTBBarcodeScannerExample/Classes/Controllers/MTBAdvancedExampleViewController.m b/Project/ObjC/MTBBarcodeScannerExample/Classes/Controllers/MTBAdvancedExampleViewController.m similarity index 100% rename from Project/MTBBarcodeScannerExample/Classes/Controllers/MTBAdvancedExampleViewController.m rename to Project/ObjC/MTBBarcodeScannerExample/Classes/Controllers/MTBAdvancedExampleViewController.m diff --git a/Project/MTBBarcodeScannerExample/Classes/Controllers/MTBBasicExampleViewController.h b/Project/ObjC/MTBBarcodeScannerExample/Classes/Controllers/MTBBasicExampleViewController.h similarity index 100% rename from Project/MTBBarcodeScannerExample/Classes/Controllers/MTBBasicExampleViewController.h rename to Project/ObjC/MTBBarcodeScannerExample/Classes/Controllers/MTBBasicExampleViewController.h diff --git a/Project/MTBBarcodeScannerExample/Classes/Controllers/MTBBasicExampleViewController.m b/Project/ObjC/MTBBarcodeScannerExample/Classes/Controllers/MTBBasicExampleViewController.m similarity index 100% rename from Project/MTBBarcodeScannerExample/Classes/Controllers/MTBBasicExampleViewController.m rename to Project/ObjC/MTBBarcodeScannerExample/Classes/Controllers/MTBBasicExampleViewController.m diff --git a/Project/MTBBarcodeScannerExample/Classes/Controllers/SwiftExampleViewController.swift b/Project/ObjC/MTBBarcodeScannerExample/Classes/Controllers/SwiftExampleViewController.swift similarity index 100% rename from Project/MTBBarcodeScannerExample/Classes/Controllers/SwiftExampleViewController.swift rename to Project/ObjC/MTBBarcodeScannerExample/Classes/Controllers/SwiftExampleViewController.swift diff --git a/Project/MTBBarcodeScannerExample/Images.xcassets/AppIcon.appiconset/Contents.json b/Project/ObjC/MTBBarcodeScannerExample/Images.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from Project/MTBBarcodeScannerExample/Images.xcassets/AppIcon.appiconset/Contents.json rename to Project/ObjC/MTBBarcodeScannerExample/Images.xcassets/AppIcon.appiconset/Contents.json diff --git a/Project/MTBBarcodeScannerExample/Images.xcassets/LaunchImage.launchimage/Contents.json b/Project/ObjC/MTBBarcodeScannerExample/Images.xcassets/LaunchImage.launchimage/Contents.json similarity index 100% rename from Project/MTBBarcodeScannerExample/Images.xcassets/LaunchImage.launchimage/Contents.json rename to Project/ObjC/MTBBarcodeScannerExample/Images.xcassets/LaunchImage.launchimage/Contents.json diff --git a/Project/MTBBarcodeScannerExample/Images.xcassets/flip.imageset/Contents.json b/Project/ObjC/MTBBarcodeScannerExample/Images.xcassets/flip.imageset/Contents.json similarity index 100% rename from Project/MTBBarcodeScannerExample/Images.xcassets/flip.imageset/Contents.json rename to Project/ObjC/MTBBarcodeScannerExample/Images.xcassets/flip.imageset/Contents.json diff --git a/Project/MTBBarcodeScannerExample/Images.xcassets/flip.imageset/flip.png b/Project/ObjC/MTBBarcodeScannerExample/Images.xcassets/flip.imageset/flip.png similarity index 100% rename from Project/MTBBarcodeScannerExample/Images.xcassets/flip.imageset/flip.png rename to Project/ObjC/MTBBarcodeScannerExample/Images.xcassets/flip.imageset/flip.png diff --git a/Project/MTBBarcodeScannerExample/Images.xcassets/flip.imageset/flip@2x.png b/Project/ObjC/MTBBarcodeScannerExample/Images.xcassets/flip.imageset/flip@2x.png similarity index 100% rename from Project/MTBBarcodeScannerExample/Images.xcassets/flip.imageset/flip@2x.png rename to Project/ObjC/MTBBarcodeScannerExample/Images.xcassets/flip.imageset/flip@2x.png diff --git a/Project/MTBBarcodeScannerExample/Images.xcassets/reverse.imageset/Contents.json b/Project/ObjC/MTBBarcodeScannerExample/Images.xcassets/reverse.imageset/Contents.json similarity index 100% rename from Project/MTBBarcodeScannerExample/Images.xcassets/reverse.imageset/Contents.json rename to Project/ObjC/MTBBarcodeScannerExample/Images.xcassets/reverse.imageset/Contents.json diff --git a/Project/MTBBarcodeScannerExample/Images.xcassets/reverse.imageset/reverse.png b/Project/ObjC/MTBBarcodeScannerExample/Images.xcassets/reverse.imageset/reverse.png similarity index 100% rename from Project/MTBBarcodeScannerExample/Images.xcassets/reverse.imageset/reverse.png rename to Project/ObjC/MTBBarcodeScannerExample/Images.xcassets/reverse.imageset/reverse.png diff --git a/Project/MTBBarcodeScannerExample/Images.xcassets/reverse.imageset/reverse@2x.png b/Project/ObjC/MTBBarcodeScannerExample/Images.xcassets/reverse.imageset/reverse@2x.png similarity index 100% rename from Project/MTBBarcodeScannerExample/Images.xcassets/reverse.imageset/reverse@2x.png rename to Project/ObjC/MTBBarcodeScannerExample/Images.xcassets/reverse.imageset/reverse@2x.png diff --git a/Project/MTBBarcodeScannerExample/MTBBarcodeScannerExample-Bridging-Header.h b/Project/ObjC/MTBBarcodeScannerExample/MTBBarcodeScannerExample-Bridging-Header.h similarity index 100% rename from Project/MTBBarcodeScannerExample/MTBBarcodeScannerExample-Bridging-Header.h rename to Project/ObjC/MTBBarcodeScannerExample/MTBBarcodeScannerExample-Bridging-Header.h diff --git a/Project/MTBBarcodeScannerExample/MTBBarcodeScannerExample-Info.plist b/Project/ObjC/MTBBarcodeScannerExample/MTBBarcodeScannerExample-Info.plist similarity index 100% rename from Project/MTBBarcodeScannerExample/MTBBarcodeScannerExample-Info.plist rename to Project/ObjC/MTBBarcodeScannerExample/MTBBarcodeScannerExample-Info.plist diff --git a/Project/MTBBarcodeScannerExample/MTBBarcodeScannerExample-Prefix.pch b/Project/ObjC/MTBBarcodeScannerExample/MTBBarcodeScannerExample-Prefix.pch similarity index 100% rename from Project/MTBBarcodeScannerExample/MTBBarcodeScannerExample-Prefix.pch rename to Project/ObjC/MTBBarcodeScannerExample/MTBBarcodeScannerExample-Prefix.pch diff --git a/Project/MTBBarcodeScannerExample/en.lproj/InfoPlist.strings b/Project/ObjC/MTBBarcodeScannerExample/en.lproj/InfoPlist.strings similarity index 100% rename from Project/MTBBarcodeScannerExample/en.lproj/InfoPlist.strings rename to Project/ObjC/MTBBarcodeScannerExample/en.lproj/InfoPlist.strings diff --git a/Project/MTBBarcodeScannerExample/main.m b/Project/ObjC/MTBBarcodeScannerExample/main.m similarity index 100% rename from Project/MTBBarcodeScannerExample/main.m rename to Project/ObjC/MTBBarcodeScannerExample/main.m diff --git a/Project/MTBBarcodeScannerExampleTests/MTBBarcodeScannerExampleTests-Info.plist b/Project/ObjC/MTBBarcodeScannerExampleTests/MTBBarcodeScannerExampleTests-Info.plist similarity index 100% rename from Project/MTBBarcodeScannerExampleTests/MTBBarcodeScannerExampleTests-Info.plist rename to Project/ObjC/MTBBarcodeScannerExampleTests/MTBBarcodeScannerExampleTests-Info.plist diff --git a/Project/MTBBarcodeScannerExampleTests/en.lproj/InfoPlist.strings b/Project/ObjC/MTBBarcodeScannerExampleTests/en.lproj/InfoPlist.strings similarity index 100% rename from Project/MTBBarcodeScannerExampleTests/en.lproj/InfoPlist.strings rename to Project/ObjC/MTBBarcodeScannerExampleTests/en.lproj/InfoPlist.strings From 99a68a62a940c29e08e8eccfd9c03261a3a7e115 Mon Sep 17 00:00:00 2001 From: Peter Gonzalez Date: Fri, 21 Aug 2020 12:21:17 -0700 Subject: [PATCH 13/30] Zoom feature --- Classes/ios/Scanners/MTBBarcodeScanner.m | 22 +++++++++++++++++++ .../project.pbxproj | 6 ++--- .../SwiftExampleViewController.swift | 6 ++--- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/Classes/ios/Scanners/MTBBarcodeScanner.m b/Classes/ios/Scanners/MTBBarcodeScanner.m index 8885b2c..db04d9c 100644 --- a/Classes/ios/Scanners/MTBBarcodeScanner.m +++ b/Classes/ios/Scanners/MTBBarcodeScanner.m @@ -301,7 +301,12 @@ - (BOOL)startScanningWithCamera:(MTBCamera)camera resultBlock:(void (^)(NSArray< // Configure the session _camera = camera; + self.captureDevice = [self newCaptureDeviceWithCamera:self.camera]; + [self.captureDevice lockForConfiguration:&error]; + [ self.captureDevice setVideoZoomFactor: 2.0]; + + [self.captureDevice unlockForConfiguration]; AVCaptureSession *session = [self newSessionWithCaptureDevice:self.captureDevice error:error]; if (!session) { @@ -333,6 +338,18 @@ - (BOOL)startScanningWithCamera:(MTBCamera)camera resultBlock:(void (^)(NSArray< // Call that block now that we've started scanning: // Dispatch back to main dispatch_async(dispatch_get_main_queue(), ^{ + NSNumber *DefaultZoomFactor = [[NSNumber alloc] initWithFloat:3.0]; + NSError *error = nil; + if ([[self captureDevice] lockForConfiguration:&error]) + { + [[self captureDevice] setVideoZoomFactor:[DefaultZoomFactor floatValue]]; + //self.zoomFactorLabel.text = [NSString stringWithFormat:@"%.1f", [DefaultZoomFactor floatValue]]; + [[self captureDevice]unlockForConfiguration]; + } + else + { + NSLog(@"%@", error); + } self.didStartScanningBlock(); }); } @@ -503,6 +520,11 @@ - (void)applicationWillEnterForegroundNotification:(NSNotification *)notificatio - (AVCaptureSession *)newSessionWithCaptureDevice:(AVCaptureDevice *)captureDevice error:(NSError **)error { AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:error]; + [captureDevice lockForConfiguration:&error]; + [ captureDevice setVideoZoomFactor:5.0]; + [captureDevice unlockForConfiguration]; + + if (!input) { // we rely on deviceInputWithDevice:error: to populate the error diff --git a/Project/MTBBarcodeScannerExample.xcodeproj/project.pbxproj b/Project/MTBBarcodeScannerExample.xcodeproj/project.pbxproj index 92bc8fb..150138f 100644 --- a/Project/MTBBarcodeScannerExample.xcodeproj/project.pbxproj +++ b/Project/MTBBarcodeScannerExample.xcodeproj/project.pbxproj @@ -332,7 +332,7 @@ LastUpgradeCheck = 1010; TargetAttributes = { 27C3BF3D18A7140F00C44713 = { - DevelopmentTeam = K83F2KZFM9; + DevelopmentTeam = 4P32224MW9; LastSwiftMigration = 1010; }; 563B264B1CD9F95F00DE7D8D = { @@ -570,7 +570,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEVELOPMENT_TEAM = K83F2KZFM9; + DEVELOPMENT_TEAM = 4P32224MW9; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "MTBBarcodeScannerExample/MTBBarcodeScannerExample-Prefix.pch"; INFOPLIST_FILE = "MTBBarcodeScannerExample/MTBBarcodeScannerExample-Info.plist"; @@ -595,7 +595,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - DEVELOPMENT_TEAM = K83F2KZFM9; + DEVELOPMENT_TEAM = 4P32224MW9; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "MTBBarcodeScannerExample/MTBBarcodeScannerExample-Prefix.pch"; INFOPLIST_FILE = "MTBBarcodeScannerExample/MTBBarcodeScannerExample-Info.plist"; diff --git a/Project/MTBBarcodeScannerExample/Classes/Controllers/SwiftExampleViewController.swift b/Project/MTBBarcodeScannerExample/Classes/Controllers/SwiftExampleViewController.swift index 8d3f09e..ac2b31a 100644 --- a/Project/MTBBarcodeScannerExample/Classes/Controllers/SwiftExampleViewController.swift +++ b/Project/MTBBarcodeScannerExample/Classes/Controllers/SwiftExampleViewController.swift @@ -31,12 +31,12 @@ class SwiftExampleViewController: UIViewController { if success { do { // Start scanning with the front camera - try self.scanner?.startScanning(with: .front, + try self.scanner?.startScanning(with: .back, resultBlock: { codes in if let codes = codes { for code in codes { - let stringValue = code.stringValue! - print("Found code: \(stringValue)") + // let stringValue = code.stringValue! + // print("Found code: \(stringValue)") } } }) From 4e9bd6776ac2fa9ea96c5fda8f235b87ea8c9ab7 Mon Sep 17 00:00:00 2001 From: Peter Gonzalez Date: Fri, 21 Aug 2020 13:01:33 -0700 Subject: [PATCH 14/30] Added tap to toggle zoom --- Classes/ios/Scanners/MTBBarcodeScanner.m | 53 ++++++++++++++++++++---- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/Classes/ios/Scanners/MTBBarcodeScanner.m b/Classes/ios/Scanners/MTBBarcodeScanner.m index db04d9c..e96cd12 100644 --- a/Classes/ios/Scanners/MTBBarcodeScanner.m +++ b/Classes/ios/Scanners/MTBBarcodeScanner.m @@ -13,6 +13,7 @@ CGFloat const kFocalPointOfInterestY = 0.5; static NSString *kErrorDomain = @"MTBBarcodeScannerError"; +static bool zoomedIn = false; // Error Codes static const NSInteger kErrorCodeStillImageCaptureInProgress = 1000; @@ -303,10 +304,7 @@ - (BOOL)startScanningWithCamera:(MTBCamera)camera resultBlock:(void (^)(NSArray< _camera = camera; self.captureDevice = [self newCaptureDeviceWithCamera:self.camera]; - [self.captureDevice lockForConfiguration:&error]; - [ self.captureDevice setVideoZoomFactor: 2.0]; - - [self.captureDevice unlockForConfiguration]; + AVCaptureSession *session = [self newSessionWithCaptureDevice:self.captureDevice error:error]; if (!session) { @@ -327,6 +325,7 @@ - (BOOL)startScanningWithCamera:(MTBCamera)camera resultBlock:(void (^)(NSArray< self.resultBlock = resultBlock; dispatch_async(self.privateSessionQueue, ^{ + // Configure the rect of interest self.captureOutput.rectOfInterest = [self rectOfInterestFromScanRect:self.scanRect]; @@ -446,6 +445,18 @@ - (void)focusTapped:(UITapGestureRecognizer *)tapGesture { device.focusPointOfInterest = devicePoint; device.focusMode = AVCaptureFocusModeContinuousAutoFocus; } + NSNumber *DefaultZoomFactor = [[NSNumber alloc] initWithFloat:3.0]; + if(!zoomedIn){ + [[self captureDevice] setVideoZoomFactor:[DefaultZoomFactor floatValue]]; + zoomedIn = true; + }else{ + DefaultZoomFactor = [[NSNumber alloc] initWithFloat:1.0]; + [[self captureDevice] setVideoZoomFactor:[DefaultZoomFactor floatValue ]]; + zoomedIn = false; + + } + + [device unlockForConfiguration]; } else { NSLog(@"Failed to acquire lock for focus change: %@", error); @@ -520,9 +531,7 @@ - (void)applicationWillEnterForegroundNotification:(NSNotification *)notificatio - (AVCaptureSession *)newSessionWithCaptureDevice:(AVCaptureDevice *)captureDevice error:(NSError **)error { AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:error]; - [captureDevice lockForConfiguration:&error]; - [ captureDevice setVideoZoomFactor:5.0]; - [captureDevice unlockForConfiguration]; + @@ -771,12 +780,38 @@ - (BOOL)setTorchMode:(MTBTorchMode)torchMode error:(NSError **)error { - (void)toggleTorch { switch (self.torchMode) { - case MTBTorchModeOn: + case MTBTorchModeOn:{ self.torchMode = MTBTorchModeOff; +// NSNumber *DefaultZoomFactor = [[NSNumber alloc] initWithFloat:1.0]; +// NSError *error = nil; +// if ([[self captureDevice] lockForConfiguration:&error]) +// { +// [[self captureDevice] setVideoZoomFactor:[DefaultZoomFactor floatValue]]; +// //self.zoomFactorLabel.text = [NSString stringWithFormat:@"%.1f", [DefaultZoomFactor floatValue]]; +// [[self captureDevice]unlockForConfiguration]; +// } +// else +// { +// NSLog(@"%@", error); +// } + } break; - case MTBTorchModeOff: + case MTBTorchModeOff:{ self.torchMode = MTBTorchModeOn; +// NSNumber *DefaultZoomFactor = [[NSNumber alloc] initWithFloat:1.0]; +// NSError *error = nil; +// if ([[self captureDevice] lockForConfiguration:&error]) +// { +// [[self captureDevice] setVideoZoomFactor:[DefaultZoomFactor floatValue]]; +// //self.zoomFactorLabel.text = [NSString stringWithFormat:@"%.1f", [DefaultZoomFactor floatValue]]; +// [[self captureDevice]unlockForConfiguration]; +// } +// else +// { +// NSLog(@"%@", error); +// } + } break; } } From 07180a428cfe100c08c068e9355524a9660abd1b Mon Sep 17 00:00:00 2001 From: Peter Gonzalez Date: Fri, 21 Aug 2020 13:26:09 -0700 Subject: [PATCH 15/30] Changed to default 2 and 6 tenths zoom --- Classes/ios/Scanners/MTBBarcodeScanner.m | 38 ++++++++++++++++-------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/Classes/ios/Scanners/MTBBarcodeScanner.m b/Classes/ios/Scanners/MTBBarcodeScanner.m index e96cd12..e3a7af1 100644 --- a/Classes/ios/Scanners/MTBBarcodeScanner.m +++ b/Classes/ios/Scanners/MTBBarcodeScanner.m @@ -328,7 +328,18 @@ - (BOOL)startScanningWithCamera:(MTBCamera)camera resultBlock:(void (^)(NSArray< // Configure the rect of interest self.captureOutput.rectOfInterest = [self rectOfInterestFromScanRect:self.scanRect]; - + NSNumber *DefaultZoomFactor = [[NSNumber alloc] initWithFloat:2.6]; + NSError *error = nil; + if ([[self captureDevice] lockForConfiguration:&error]) + { + [[self captureDevice] setVideoZoomFactor:[DefaultZoomFactor floatValue]]; + //self.zoomFactorLabel.text = [NSString stringWithFormat:@"%.1f", [DefaultZoomFactor floatValue]]; + [[self captureDevice]unlockForConfiguration]; + } + else + { + NSLog(@"%@", error); + } // Start the session after all configurations: // Must be dispatched as it is blocking [self.session startRunning]; @@ -337,7 +348,8 @@ - (BOOL)startScanningWithCamera:(MTBCamera)camera resultBlock:(void (^)(NSArray< // Call that block now that we've started scanning: // Dispatch back to main dispatch_async(dispatch_get_main_queue(), ^{ - NSNumber *DefaultZoomFactor = [[NSNumber alloc] initWithFloat:3.0]; + self.didStartScanningBlock(); + NSNumber *DefaultZoomFactor = [[NSNumber alloc] initWithFloat:2.6]; NSError *error = nil; if ([[self captureDevice] lockForConfiguration:&error]) { @@ -349,7 +361,7 @@ - (BOOL)startScanningWithCamera:(MTBCamera)camera resultBlock:(void (^)(NSArray< { NSLog(@"%@", error); } - self.didStartScanningBlock(); + }); } }); @@ -445,16 +457,16 @@ - (void)focusTapped:(UITapGestureRecognizer *)tapGesture { device.focusPointOfInterest = devicePoint; device.focusMode = AVCaptureFocusModeContinuousAutoFocus; } - NSNumber *DefaultZoomFactor = [[NSNumber alloc] initWithFloat:3.0]; - if(!zoomedIn){ - [[self captureDevice] setVideoZoomFactor:[DefaultZoomFactor floatValue]]; - zoomedIn = true; - }else{ - DefaultZoomFactor = [[NSNumber alloc] initWithFloat:1.0]; - [[self captureDevice] setVideoZoomFactor:[DefaultZoomFactor floatValue ]]; - zoomedIn = false; - - } +// NSNumber *DefaultZoomFactor = [[NSNumber alloc] initWithFloat:3.0]; +// if(!zoomedIn){ +// [[self captureDevice] setVideoZoomFactor:[DefaultZoomFactor floatValue]]; +// zoomedIn = true; +// }else{ +// DefaultZoomFactor = [[NSNumber alloc] initWithFloat:1.0]; +// [[self captureDevice] setVideoZoomFactor:[DefaultZoomFactor floatValue ]]; +// zoomedIn = false; +// +// } [device unlockForConfiguration]; From 1594da21623607c6e5f3e20e91387df2ce5c95df Mon Sep 17 00:00:00 2001 From: Peter Gonzalez Date: Fri, 21 Aug 2020 13:28:49 -0700 Subject: [PATCH 16/30] Cleaned up the code --- Classes/ios/Scanners/MTBBarcodeScanner.m | 37 +----------------------- 1 file changed, 1 insertion(+), 36 deletions(-) diff --git a/Classes/ios/Scanners/MTBBarcodeScanner.m b/Classes/ios/Scanners/MTBBarcodeScanner.m index e3a7af1..6a629a7 100644 --- a/Classes/ios/Scanners/MTBBarcodeScanner.m +++ b/Classes/ios/Scanners/MTBBarcodeScanner.m @@ -13,7 +13,6 @@ CGFloat const kFocalPointOfInterestY = 0.5; static NSString *kErrorDomain = @"MTBBarcodeScannerError"; -static bool zoomedIn = false; // Error Codes static const NSInteger kErrorCodeStillImageCaptureInProgress = 1000; @@ -354,7 +353,6 @@ - (BOOL)startScanningWithCamera:(MTBCamera)camera resultBlock:(void (^)(NSArray< if ([[self captureDevice] lockForConfiguration:&error]) { [[self captureDevice] setVideoZoomFactor:[DefaultZoomFactor floatValue]]; - //self.zoomFactorLabel.text = [NSString stringWithFormat:@"%.1f", [DefaultZoomFactor floatValue]]; [[self captureDevice]unlockForConfiguration]; } else @@ -457,16 +455,6 @@ - (void)focusTapped:(UITapGestureRecognizer *)tapGesture { device.focusPointOfInterest = devicePoint; device.focusMode = AVCaptureFocusModeContinuousAutoFocus; } -// NSNumber *DefaultZoomFactor = [[NSNumber alloc] initWithFloat:3.0]; -// if(!zoomedIn){ -// [[self captureDevice] setVideoZoomFactor:[DefaultZoomFactor floatValue]]; -// zoomedIn = true; -// }else{ -// DefaultZoomFactor = [[NSNumber alloc] initWithFloat:1.0]; -// [[self captureDevice] setVideoZoomFactor:[DefaultZoomFactor floatValue ]]; -// zoomedIn = false; -// -// } [device unlockForConfiguration]; @@ -794,35 +782,12 @@ - (void)toggleTorch { switch (self.torchMode) { case MTBTorchModeOn:{ self.torchMode = MTBTorchModeOff; -// NSNumber *DefaultZoomFactor = [[NSNumber alloc] initWithFloat:1.0]; -// NSError *error = nil; -// if ([[self captureDevice] lockForConfiguration:&error]) -// { -// [[self captureDevice] setVideoZoomFactor:[DefaultZoomFactor floatValue]]; -// //self.zoomFactorLabel.text = [NSString stringWithFormat:@"%.1f", [DefaultZoomFactor floatValue]]; -// [[self captureDevice]unlockForConfiguration]; -// } -// else -// { -// NSLog(@"%@", error); -// } + } break; case MTBTorchModeOff:{ self.torchMode = MTBTorchModeOn; -// NSNumber *DefaultZoomFactor = [[NSNumber alloc] initWithFloat:1.0]; -// NSError *error = nil; -// if ([[self captureDevice] lockForConfiguration:&error]) -// { -// [[self captureDevice] setVideoZoomFactor:[DefaultZoomFactor floatValue]]; -// //self.zoomFactorLabel.text = [NSString stringWithFormat:@"%.1f", [DefaultZoomFactor floatValue]]; -// [[self captureDevice]unlockForConfiguration]; -// } -// else -// { -// NSLog(@"%@", error); -// } } break; } From d941e2594101d6f7cfeeb713ecae3912ad1d2361 Mon Sep 17 00:00:00 2001 From: Mike Buss Date: Wed, 30 Sep 2020 20:31:33 -0400 Subject: [PATCH 17/30] Updated readme to reflect maintenance status --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index ca8f70b..4fde24b 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +# This library is no longer actively maintained. If you are interested in maintaining it, please contact [the original author](mailto:mike@mikebuss.com). + # MTBBarcodeScanner [![Version](https://img.shields.io/cocoapods/v/MTBBarcodeScanner.svg?style=flat)](http://cocoadocs.org/docsets/MTBBarcodeScanner) From a00f50a74954b5edc2a972ec1c4b5dc46bc9818c Mon Sep 17 00:00:00 2001 From: "thoson.it" Date: Fri, 2 Oct 2020 11:13:18 +0700 Subject: [PATCH 18/30] change AVCaptureSession SessionPreset from AVCaptureSessionPresetHigh to AVCaptureSessionPresetPhoto --- Classes/ios/Scanners/MTBBarcodeScanner.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Classes/ios/Scanners/MTBBarcodeScanner.m b/Classes/ios/Scanners/MTBBarcodeScanner.m index 8885b2c..7ea141c 100644 --- a/Classes/ios/Scanners/MTBBarcodeScanner.m +++ b/Classes/ios/Scanners/MTBBarcodeScanner.m @@ -513,7 +513,7 @@ - (AVCaptureSession *)newSessionWithCaptureDevice:(AVCaptureDevice *)captureDevi [self setDeviceInput:input session:newSession]; // Set an optimized preset for barcode scanning - [newSession setSessionPreset:AVCaptureSessionPresetHigh]; + [newSession setSessionPreset:AVCaptureSessionPresetPhoto]; self.captureOutput = [[AVCaptureMetadataOutput alloc] init]; [self.captureOutput setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()]; From a7267523de83c82b4d0eafe8293fd778390f533d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=BE=AE?= <> Date: Sat, 24 Oct 2020 17:09:49 +0800 Subject: [PATCH 19/30] =?UTF-8?q?=E4=BF=AE=E6=94=B9AVCaptureSession?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Classes/ios/Scanners/MTBBarcodeScanner.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Classes/ios/Scanners/MTBBarcodeScanner.m b/Classes/ios/Scanners/MTBBarcodeScanner.m index 8885b2c..340bb70 100644 --- a/Classes/ios/Scanners/MTBBarcodeScanner.m +++ b/Classes/ios/Scanners/MTBBarcodeScanner.m @@ -513,7 +513,7 @@ - (AVCaptureSession *)newSessionWithCaptureDevice:(AVCaptureDevice *)captureDevi [self setDeviceInput:input session:newSession]; // Set an optimized preset for barcode scanning - [newSession setSessionPreset:AVCaptureSessionPresetHigh]; + [newSession setSessionPreset:AVCaptureSessionPreset1920x1080]; self.captureOutput = [[AVCaptureMetadataOutput alloc] init]; [self.captureOutput setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()]; From 6e206c7300c85a0f6a543eebccc68860624146e1 Mon Sep 17 00:00:00 2001 From: xueminlee Date: Fri, 12 Mar 2021 17:45:28 +0800 Subject: [PATCH 20/30] Update MTBBarcodeScanner.m --- Classes/ios/Scanners/MTBBarcodeScanner.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Classes/ios/Scanners/MTBBarcodeScanner.m b/Classes/ios/Scanners/MTBBarcodeScanner.m index 8885b2c..14392fa 100644 --- a/Classes/ios/Scanners/MTBBarcodeScanner.m +++ b/Classes/ios/Scanners/MTBBarcodeScanner.m @@ -199,7 +199,7 @@ + (BOOL)cameraIsPresent { + (BOOL)hasCamera:(MTBCamera)camera { AVCaptureDevicePosition position = [self devicePositionForCamera:camera]; - if (@available(iOS 10.0, *)) { + if (@available(iOS 10.0.1, *)) { AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInWideAngleCamera mediaType:AVMediaTypeVideo position:position]; @@ -565,7 +565,7 @@ - (AVCaptureDevice *)newCaptureDeviceWithCamera:(MTBCamera)camera { AVCaptureDevice *newCaptureDevice = nil; AVCaptureDevicePosition position = [[self class] devicePositionForCamera:camera]; - if (@available(iOS 10.0, *)) { + if (@available(iOS 10.0.1, *)) { AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInWideAngleCamera mediaType:AVMediaTypeVideo position:position]; From dc3861fccefc2fbfa349fb6deba69ab855b01a96 Mon Sep 17 00:00:00 2001 From: Andrew Barba Date: Sun, 28 Mar 2021 23:39:58 -0400 Subject: [PATCH 21/30] Create Package.swift --- Package.swift | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Package.swift diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..77cd522 --- /dev/null +++ b/Package.swift @@ -0,0 +1,20 @@ +// swift-tools-version:5.0 +import PackageDescription + +let package = Package( + name: "MTBBarcodeScanner", + platforms: [.iOS(.v9)], + products: [ + .library( + name: "MTBBarcodeScanner", + targets: ["MTBBarcodeScanner"] + ) + ], + targets: [ + .target( + name: "MTBBarcodeScanner", + path: "Classes/ios", + publicHeadersPath: "." + ) + ] +) From 213579368bdc1ca4bae615982ad4bcc7d9e43df9 Mon Sep 17 00:00:00 2001 From: Andrew Barba Date: Sun, 28 Mar 2021 23:45:37 -0400 Subject: [PATCH 22/30] Update Package.swift --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 77cd522..7156446 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.0 +// swift-tools-version:5.3 import PackageDescription let package = Package( From 3c9ebb24f1d582da67aa4cd5490ef020c3f74ae7 Mon Sep 17 00:00:00 2001 From: Andrew Reeman Date: Sat, 11 Sep 2021 10:33:25 +0100 Subject: [PATCH 23/30] Removed potentially invalid path --- Sources/includes/MTBBarcode.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/includes/MTBBarcode.h b/Sources/includes/MTBBarcode.h index a59bce2..d7541f7 100644 --- a/Sources/includes/MTBBarcode.h +++ b/Sources/includes/MTBBarcode.h @@ -15,4 +15,4 @@ FOUNDATION_EXPORT double MTBBarcodeScannerVersionNumber; FOUNDATION_EXPORT const unsigned char MTBBarcodeScannerVersionString[]; // In this header, you should import all the public headers of your framework using statements like #import -#import +#import From ab73b18884b553acccba274abacea74c7bf29467 Mon Sep 17 00:00:00 2001 From: Andrew Reeman Date: Sat, 11 Sep 2021 11:26:56 +0100 Subject: [PATCH 24/30] exposing local file. perhaps this works on ci --- Sources/includes/MTBBarcode.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/includes/MTBBarcode.h b/Sources/includes/MTBBarcode.h index d7541f7..822bb51 100644 --- a/Sources/includes/MTBBarcode.h +++ b/Sources/includes/MTBBarcode.h @@ -15,4 +15,4 @@ FOUNDATION_EXPORT double MTBBarcodeScannerVersionNumber; FOUNDATION_EXPORT const unsigned char MTBBarcodeScannerVersionString[]; // In this header, you should import all the public headers of your framework using statements like #import -#import +#import "MTBBarcodeScanner.h" From 82d6d3a96b4d711c60add0083a842267c0f0dc58 Mon Sep 17 00:00:00 2001 From: Shan Haq Date: Sun, 6 Feb 2022 12:39:29 +1100 Subject: [PATCH 25/30] fix: added check to drop frames while capturing images --- Classes/ios/Scanners/MTBBarcodeScanner.m | 1 + 1 file changed, 1 insertion(+) diff --git a/Classes/ios/Scanners/MTBBarcodeScanner.m b/Classes/ios/Scanners/MTBBarcodeScanner.m index 8885b2c..179ebc3 100644 --- a/Classes/ios/Scanners/MTBBarcodeScanner.m +++ b/Classes/ios/Scanners/MTBBarcodeScanner.m @@ -449,6 +449,7 @@ - (void)stopRecognizingTaps { - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection { if (!self.resultBlock) return; + if (self.isCapturingStillImage) return; NSMutableArray *codes = [[NSMutableArray alloc] init]; From 307a3264506986a03b794973922470b1bc5415fa Mon Sep 17 00:00:00 2001 From: Shan Haq Date: Sun, 6 Feb 2022 12:43:23 +1100 Subject: [PATCH 26/30] updated podspec --- MTBBarcodeScanner.podspec | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/MTBBarcodeScanner.podspec b/MTBBarcodeScanner.podspec index c57f46d..df494ab 100644 --- a/MTBBarcodeScanner.podspec +++ b/MTBBarcodeScanner.podspec @@ -1,11 +1,11 @@ Pod::Spec.new do |s| - s.name = "MTBBarcodeScanner" - s.version = "5.0.11" + s.name = "MTBBarcodeScanner2" + s.version = "5.0.12" s.summary = "A lightweight, easy-to-use barcode scanning library for iOS 8+." - s.homepage = "https://github.com/mikebuss/MTBBarcodeScanner" + s.homepage = "https://github.com/grevolution/MTBBarcodeScanner" s.license = 'MIT' - s.author = { "Mike Buss" => "mike@mikebuss.com" } - s.source = { :git => "https://github.com/mikebuss/MTBBarcodeScanner.git", :tag => s.version.to_s } + s.author = { "Shan Haq" => "g@grevolution.me" } + s.source = { :git => "https://github.com/grevolution/MTBBarcodeScanner.git", :tag => s.version.to_s } s.platform = :ios, '8.0' s.ios.deployment_target = '8.0' From e4868b1a5e801d8306449a7a87149001532a5eb9 Mon Sep 17 00:00:00 2001 From: Shan Haq Date: Sun, 6 Feb 2022 12:46:35 +1100 Subject: [PATCH 27/30] updated version and name --- .ruby-version | 2 +- MTBBarcodeScanner.podspec => MTBBarcodeScanner2.podspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename MTBBarcodeScanner.podspec => MTBBarcodeScanner2.podspec (95%) diff --git a/.ruby-version b/.ruby-version index 73462a5..a4dd9db 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.5.1 +2.7.4 diff --git a/MTBBarcodeScanner.podspec b/MTBBarcodeScanner2.podspec similarity index 95% rename from MTBBarcodeScanner.podspec rename to MTBBarcodeScanner2.podspec index df494ab..5f6d155 100644 --- a/MTBBarcodeScanner.podspec +++ b/MTBBarcodeScanner2.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "MTBBarcodeScanner2" - s.version = "5.0.12" + s.version = "5.0.13" s.summary = "A lightweight, easy-to-use barcode scanning library for iOS 8+." s.homepage = "https://github.com/grevolution/MTBBarcodeScanner" s.license = 'MIT' From 7c96133fbcae1269cba6a02da52557fe946176fc Mon Sep 17 00:00:00 2001 From: lardieri Date: Sun, 13 Mar 2022 08:29:38 -0700 Subject: [PATCH 28/30] Torch: set specific level, not just on/off. --- Classes/ios/Scanners/MTBBarcodeScanner.h | 11 ++++++++ Classes/ios/Scanners/MTBBarcodeScanner.m | 34 ++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/Classes/ios/Scanners/MTBBarcodeScanner.h b/Classes/ios/Scanners/MTBBarcodeScanner.h index 616802e..55daab9 100644 --- a/Classes/ios/Scanners/MTBBarcodeScanner.h +++ b/Classes/ios/Scanners/MTBBarcodeScanner.h @@ -300,6 +300,17 @@ typedef NS_ENUM(NSUInteger, MTBTorchMode) { */ - (BOOL)setTorchMode:(MTBTorchMode)torchMode error:(NSError **)error; +/** + * Attempts to set a new torch level. + * + * @return YES, if setting the new level was successful, and the torchMode + * property reflects the new state. NO if there was an error - use the + * error parameter to learn about the reason. + * + * @sa torchMode + */ +- (BOOL)setTorchLevel:(float)torchLevel error:(NSError **)error; + /** * Freeze capture keeping the last frame on previewView. * If this method is called before startScanning, it has no effect. diff --git a/Classes/ios/Scanners/MTBBarcodeScanner.m b/Classes/ios/Scanners/MTBBarcodeScanner.m index 8885b2c..a36f68b 100644 --- a/Classes/ios/Scanners/MTBBarcodeScanner.m +++ b/Classes/ios/Scanners/MTBBarcodeScanner.m @@ -747,6 +747,16 @@ - (BOOL)setTorchMode:(MTBTorchMode)torchMode error:(NSError **)error { return NO; } +- (BOOL)setTorchLevel:(float)torchLevel error:(NSError **)error { + if ([self updateForTorchLevel:torchLevel error:error]) { + // we only update our internal state if setting the torch mode was successful + _torchMode = MTBTorchModeOn; + return YES; + } + + return NO; +} + - (void)toggleTorch { switch (self.torchMode) { case MTBTorchModeOn: @@ -784,6 +794,30 @@ - (BOOL)updateForTorchMode:(MTBTorchMode)preferredTorchMode error:(NSError **)er return YES; } +- (BOOL)updateForTorchLevel:(float)preferredTorchLevel error:(NSError **)error { + AVCaptureDevice *backCamera = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo]; + + if (!([backCamera isTorchAvailable] && [backCamera isTorchModeSupported:AVCaptureTorchModeOn])) { + if (error) { + *error = [NSError errorWithDomain:kErrorDomain + code:kErrorCodeTorchModeUnavailable + userInfo:@{NSLocalizedDescriptionKey : @"Torch unavailable or mode not supported."}]; + } + + return NO; + } + + if (![backCamera lockForConfiguration:error]) { + NSLog(@"Failed to acquire lock to update torch mode."); + return NO; + } + + BOOL result = [backCamera setTorchModeOnWithLevel:preferredTorchLevel error:error]; + [backCamera unlockForConfiguration]; + + return result; +} + - (BOOL)hasTorch { AVCaptureDevice *captureDevice = [self newCaptureDeviceWithCamera:self.camera]; NSError *error = nil; From 6b66b7a0c7b83f46be2d380208d0fe99a080aa85 Mon Sep 17 00:00:00 2001 From: r72-engineering <78537436+r72-engineering@users.noreply.github.com> Date: Tue, 20 Dec 2022 01:17:10 -0800 Subject: [PATCH 29/30] Fix iOS 13 deprecation warnings about status bar orientation. --- Classes/ios/Scanners/MTBBarcodeScanner.m | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Classes/ios/Scanners/MTBBarcodeScanner.m b/Classes/ios/Scanners/MTBBarcodeScanner.m index 179ebc3..db7ed11 100644 --- a/Classes/ios/Scanners/MTBBarcodeScanner.m +++ b/Classes/ios/Scanners/MTBBarcodeScanner.m @@ -465,12 +465,21 @@ - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects: #pragma mark - Rotation -- (void)handleApplicationDidChangeStatusBarNotification:(NSNotification *)notification { +- (void)handleOrientationChangeNotification:(NSNotification *)notification { [self refreshVideoOrientation]; } - (void)refreshVideoOrientation { - UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; + UIInterfaceOrientation orientation; + if (@available(iOS 13.0, *)) { + orientation = self.previewView.window.windowScene.interfaceOrientation; + } else { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + orientation = [UIApplication sharedApplication].statusBarOrientation; +#pragma GCC diagnostic pop + } + self.capturePreviewLayer.frame = self.previewView.bounds; if ([self.capturePreviewLayer.connection isVideoOrientationSupported]) { self.capturePreviewLayer.connection.videoOrientation = [self captureOrientationForInterfaceOrientation:orientation]; @@ -645,8 +654,8 @@ + (AVCaptureDevicePosition)devicePositionForCamera:(MTBCamera)camera { - (void)addObservers { [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleApplicationDidChangeStatusBarNotification:) - name:UIApplicationDidChangeStatusBarOrientationNotification + selector:@selector(handleOrientationChangeNotification:) + name:UIDeviceOrientationDidChangeNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self From 28fe5399cdcf2891706699d02b16cf6a677fa393 Mon Sep 17 00:00:00 2001 From: r72-engineering <78537436+r72-engineering@users.noreply.github.com> Date: Tue, 20 Dec 2022 01:17:57 -0800 Subject: [PATCH 30/30] Fix iOS 13 deprecation warning about autoStillImageStabilization. --- Classes/ios/Scanners/MTBBarcodeScanner.m | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Classes/ios/Scanners/MTBBarcodeScanner.m b/Classes/ios/Scanners/MTBBarcodeScanner.m index db7ed11..e73e997 100644 --- a/Classes/ios/Scanners/MTBBarcodeScanner.m +++ b/Classes/ios/Scanners/MTBBarcodeScanner.m @@ -856,20 +856,24 @@ - (void)captureStillImage:(void (^)(UIImage *image, NSError *error))captureBlock return; } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" if (@available(iOS 10.0, *)) { AVCapturePhotoSettings *settings = [AVCapturePhotoSettings photoSettings]; - settings.autoStillImageStabilizationEnabled = NO; settings.flashMode = AVCaptureFlashModeOff; settings.highResolutionPhotoEnabled = YES; - + + if (@available(iOS 13.0, *)) { + settings.photoQualityPrioritization = AVCapturePhotoQualityPrioritizationSpeed; + } else { + settings.autoStillImageStabilizationEnabled = NO; + } + dispatch_async(self.privateSessionQueue, ^{ [self.output capturePhotoWithSettings:settings delegate:self]; self.stillImageCaptureBlock = captureBlock; - }); } else { -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" AVCaptureConnection *stillConnection = [self.stillImageOutput connectionWithMediaType:AVMediaTypeVideo]; if (stillConnection == nil) { if (captureBlock) { @@ -894,8 +898,9 @@ - (void)captureStillImage:(void (^)(UIImage *image, NSError *error))captureBlock captureBlock(image, nil); } }]; -#pragma GCC diagnostic pop } +#pragma GCC diagnostic pop + } #pragma mark - AVCapturePhotoCaptureDelegate