From 5eb79457bd6405f6e4e2ae33a373a9636d18e567 Mon Sep 17 00:00:00 2001 From: breadone Date: Wed, 3 Jul 2024 17:51:16 +1200 Subject: [PATCH] Added the document camera controller and very basic mockup of the view --- .gitignore | 1 + JustScanIt.xcodeproj/project.pbxproj | 38 ++++++++++-- JustScanIt/ContentView.swift | 60 ++++++++++++++++--- .../VNDocumentCameraViewControllerR.swift | 51 ++++++++++++++++ 4 files changed, 137 insertions(+), 13 deletions(-) create mode 100644 .gitignore create mode 100644 JustScanIt/Controllers/VNDocumentCameraViewControllerR.swift diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..527b85c --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +Build/ diff --git a/JustScanIt.xcodeproj/project.pbxproj b/JustScanIt.xcodeproj/project.pbxproj index 2e44dc7..908f6ae 100644 --- a/JustScanIt.xcodeproj/project.pbxproj +++ b/JustScanIt.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 466989C92C34DC7A009884D1 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 466989C82C34DC7A009884D1 /* ContentView.swift */; }; 466989CB2C34DC7B009884D1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 466989CA2C34DC7B009884D1 /* Assets.xcassets */; }; 466989CE2C34DC7B009884D1 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 466989CD2C34DC7B009884D1 /* Preview Assets.xcassets */; }; + 466989D62C34DCE4009884D1 /* VNDocumentCameraViewControllerR.swift in Sources */ = {isa = PBXBuildFile; fileRef = 466989D52C34DCE4009884D1 /* VNDocumentCameraViewControllerR.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -19,6 +20,7 @@ 466989C82C34DC7A009884D1 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 466989CA2C34DC7B009884D1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 466989CD2C34DC7B009884D1 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 466989D52C34DCE4009884D1 /* VNDocumentCameraViewControllerR.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VNDocumentCameraViewControllerR.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -51,6 +53,7 @@ 466989C52C34DC7A009884D1 /* JustScanIt */ = { isa = PBXGroup; children = ( + 466989D42C34DCB9009884D1 /* Controllers */, 466989C62C34DC7A009884D1 /* JustScanItApp.swift */, 466989C82C34DC7A009884D1 /* ContentView.swift */, 466989CA2C34DC7B009884D1 /* Assets.xcassets */, @@ -67,6 +70,14 @@ path = "Preview Content"; sourceTree = ""; }; + 466989D42C34DCB9009884D1 /* Controllers */ = { + isa = PBXGroup; + children = ( + 466989D52C34DCE4009884D1 /* VNDocumentCameraViewControllerR.swift */, + ); + path = Controllers; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -139,6 +150,7 @@ files = ( 466989C92C34DC7A009884D1 /* ContentView.swift in Sources */, 466989C72C34DC7A009884D1 /* JustScanItApp.swift in Sources */, + 466989D62C34DCE4009884D1 /* VNDocumentCameraViewControllerR.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -275,11 +287,14 @@ DEVELOPMENT_TEAM = 9MP5435PRF; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = "Just Scan It"; + INFOPLIST_KEY_NSCameraUsageDescription = "Scan documents with the camera."; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -287,9 +302,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = xyz.breadone.JustScanIt; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; @@ -304,11 +323,14 @@ DEVELOPMENT_TEAM = 9MP5435PRF; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = "Just Scan It"; + INFOPLIST_KEY_NSCameraUsageDescription = "Scan documents with the camera."; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; - INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -316,9 +338,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = xyz.breadone.JustScanIt; PRODUCT_NAME = "$(TARGET_NAME)"; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; diff --git a/JustScanIt/ContentView.swift b/JustScanIt/ContentView.swift index e091180..5aec95a 100644 --- a/JustScanIt/ContentView.swift +++ b/JustScanIt/ContentView.swift @@ -6,16 +6,62 @@ // import SwiftUI +import AVKit struct ContentView: View { - var body: some View { - VStack { - Image(systemName: "globe") - .imageScale(.large) - .foregroundStyle(.tint) - Text("Hello, world!") + @State private var scannedImages: [UIImage] = [] + @State private var isShowingVNDocumentCameraView = false + + var isAuthorized: Bool { + get async { + let status = AVCaptureDevice.authorizationStatus(for: .video) + + // Determine if the user previously authorized camera access. + var isAuthorized = status == .authorized + + // If the system hasn't determined the user's authorization status, + // explicitly prompt them for approval. + if status == .notDetermined { + isAuthorized = await AVCaptureDevice.requestAccess(for: .video) + } + + return isAuthorized } - .padding() + } + + var body: some View { + NavigationView { + Grid { + ForEach(scannedImages, id: \.self) { image in + VStack { + Image(uiImage: image) + .resizable() + .scaledToFit() + .frame(width: 100, height: 100) + .padding() + } + } + } + .navigationTitle("Just Scan It!") + .sheet(isPresented: $isShowingVNDocumentCameraView) { + VNDocumentCameraViewControllerRepresentable(scanResult: $scannedImages) + } + .toolbar { + ToolbarItem { + Button(action: showVNDocumentCameraView) { + Image(systemName: "scanner") + } + } + } + } + } + + private func showVNDocumentCameraView() { + Task { + guard await isAuthorized else { return } + } + + isShowingVNDocumentCameraView = true } } diff --git a/JustScanIt/Controllers/VNDocumentCameraViewControllerR.swift b/JustScanIt/Controllers/VNDocumentCameraViewControllerR.swift new file mode 100644 index 0000000..19325df --- /dev/null +++ b/JustScanIt/Controllers/VNDocumentCameraViewControllerR.swift @@ -0,0 +1,51 @@ +// +// VNDocumentCameraViewControllerR.swift +// JustScanIt +// +// Created by Pradyun Setti on 03/07/2024. +// + +import SwiftUI +import VisionKit + +struct VNDocumentCameraViewControllerRepresentable: UIViewControllerRepresentable { + @Binding var scanResult: [UIImage] + + func makeUIViewController(context: Context) -> VNDocumentCameraViewController { + let documentCameraViewController = VNDocumentCameraViewController() + documentCameraViewController.delegate = context.coordinator + + return documentCameraViewController + } + + func updateUIViewController(_ uiViewController: VNDocumentCameraViewController, context: Context) { } + + func makeCoordinator() -> Coordinator { + Coordinator(scanResult: $scanResult) + } + + final class Coordinator: NSObject, VNDocumentCameraViewControllerDelegate { + @Binding var scanResult: [UIImage] + + init(scanResult: Binding<[UIImage]>) { + _scanResult = scanResult + } + + /// Tells the delegate that the user successfully saved a scanned document from the document camera. + func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFinishWith scan: VNDocumentCameraScan) { + controller.dismiss(animated: true, completion: nil) + scanResult = (0..