From 903414eedc28fa75ca0924e60c1946aacc85d699 Mon Sep 17 00:00:00 2001 From: Andrew Walz Date: Sun, 25 Dec 2016 16:59:29 -1000 Subject: [PATCH] Initial commit for reupload --- .../DemoSwiftyCam.xcodeproj/project.pbxproj | 336 +++++++++++ .../contents.xcworkspacedata | 7 + .../UserInterfaceState.xcuserstate | Bin 0 -> 19941 bytes .../xcschemes/DemoSwiftyCam.xcscheme | 91 +++ .../xcschemes/xcschememanagement.plist | 22 + DemoSwiftyCam/DemoSwiftyCam/AppDelegate.swift | 54 ++ .../AppIcon.appiconset/Contents.json | 48 ++ .../Camera.imageset/Camera@2x.png | Bin 0 -> 7324 bytes .../Camera.imageset/Camera@3x.png | Bin 0 -> 11432 bytes .../Camera.imageset/Contents.json | 22 + .../Assets.xcassets/Contents.json | 6 + .../Flash.imageset/Contents.json | 22 + .../Flash.imageset/Flash@3x.png | Bin 0 -> 2073 bytes .../Flash.imageset/flash@2x.png | Bin 0 -> 1380 bytes .../flipCamera.imageset/Contents.json | 22 + .../flipCamera.imageset/flipCamera@3x.png | Bin 0 -> 2350 bytes .../flipCamera.imageset/flipcamera@2x.png | Bin 0 -> 1649 bytes .../Base.lproj/LaunchScreen.storyboard | 27 + .../DemoSwiftyCam/Base.lproj/Main.storyboard | 60 ++ DemoSwiftyCam/DemoSwiftyCam/Info.plist | 42 ++ .../DemoSwiftyCam/ViewController.swift | 83 +++ LICENSE | 22 + README.md | 205 +++++++ Source/PreviewView.swift | 49 ++ Source/SwiftyCamButton.swift | 82 +++ Source/SwiftyCamViewController.swift | 536 ++++++++++++++++++ Source/SwiftyCamViewControllerDelegate.swift | 65 +++ SwiftyCam.podspec | 44 ++ 28 files changed, 1845 insertions(+) create mode 100644 DemoSwiftyCam/DemoSwiftyCam.xcodeproj/project.pbxproj create mode 100644 DemoSwiftyCam/DemoSwiftyCam.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 DemoSwiftyCam/DemoSwiftyCam.xcodeproj/project.xcworkspace/xcuserdata/DARKPR0.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 DemoSwiftyCam/DemoSwiftyCam.xcodeproj/xcuserdata/DARKPR0.xcuserdatad/xcschemes/DemoSwiftyCam.xcscheme create mode 100644 DemoSwiftyCam/DemoSwiftyCam.xcodeproj/xcuserdata/DARKPR0.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 DemoSwiftyCam/DemoSwiftyCam/AppDelegate.swift create mode 100644 DemoSwiftyCam/DemoSwiftyCam/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 DemoSwiftyCam/DemoSwiftyCam/Assets.xcassets/Camera.imageset/Camera@2x.png create mode 100644 DemoSwiftyCam/DemoSwiftyCam/Assets.xcassets/Camera.imageset/Camera@3x.png create mode 100644 DemoSwiftyCam/DemoSwiftyCam/Assets.xcassets/Camera.imageset/Contents.json create mode 100644 DemoSwiftyCam/DemoSwiftyCam/Assets.xcassets/Contents.json create mode 100644 DemoSwiftyCam/DemoSwiftyCam/Assets.xcassets/Flash.imageset/Contents.json create mode 100644 DemoSwiftyCam/DemoSwiftyCam/Assets.xcassets/Flash.imageset/Flash@3x.png create mode 100644 DemoSwiftyCam/DemoSwiftyCam/Assets.xcassets/Flash.imageset/flash@2x.png create mode 100644 DemoSwiftyCam/DemoSwiftyCam/Assets.xcassets/flipCamera.imageset/Contents.json create mode 100644 DemoSwiftyCam/DemoSwiftyCam/Assets.xcassets/flipCamera.imageset/flipCamera@3x.png create mode 100644 DemoSwiftyCam/DemoSwiftyCam/Assets.xcassets/flipCamera.imageset/flipcamera@2x.png create mode 100644 DemoSwiftyCam/DemoSwiftyCam/Base.lproj/LaunchScreen.storyboard create mode 100644 DemoSwiftyCam/DemoSwiftyCam/Base.lproj/Main.storyboard create mode 100644 DemoSwiftyCam/DemoSwiftyCam/Info.plist create mode 100644 DemoSwiftyCam/DemoSwiftyCam/ViewController.swift create mode 100644 LICENSE create mode 100644 README.md create mode 100644 Source/PreviewView.swift create mode 100644 Source/SwiftyCamButton.swift create mode 100644 Source/SwiftyCamViewController.swift create mode 100644 Source/SwiftyCamViewControllerDelegate.swift create mode 100644 SwiftyCam.podspec diff --git a/DemoSwiftyCam/DemoSwiftyCam.xcodeproj/project.pbxproj b/DemoSwiftyCam/DemoSwiftyCam.xcodeproj/project.pbxproj new file mode 100644 index 0000000..c34f98e --- /dev/null +++ b/DemoSwiftyCam/DemoSwiftyCam.xcodeproj/project.pbxproj @@ -0,0 +1,336 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 1675A9761E00A68300B80903 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1675A9751E00A68300B80903 /* AppDelegate.swift */; }; + 1675A9781E00A68300B80903 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1675A9771E00A68300B80903 /* ViewController.swift */; }; + 1675A97B1E00A68300B80903 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1675A9791E00A68300B80903 /* Main.storyboard */; }; + 1675A97D1E00A68300B80903 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1675A97C1E00A68300B80903 /* Assets.xcassets */; }; + 1675A9801E00A68300B80903 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1675A97E1E00A68300B80903 /* LaunchScreen.storyboard */; }; + 1675A98D1E00A74A00B80903 /* PreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1675A9891E00A74A00B80903 /* PreviewView.swift */; }; + 1675A98E1E00A74A00B80903 /* SwiftyCamButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1675A98A1E00A74A00B80903 /* SwiftyCamButton.swift */; }; + 1675A98F1E00A74A00B80903 /* SwiftyCamViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1675A98B1E00A74A00B80903 /* SwiftyCamViewController.swift */; }; + 1675A9901E00A74A00B80903 /* SwiftyCamViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1675A98C1E00A74A00B80903 /* SwiftyCamViewControllerDelegate.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 1675A9721E00A68300B80903 /* DemoSwiftyCam.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DemoSwiftyCam.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 1675A9751E00A68300B80903 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 1675A9771E00A68300B80903 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 1675A97A1E00A68300B80903 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 1675A97C1E00A68300B80903 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 1675A97F1E00A68300B80903 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 1675A9811E00A68300B80903 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 1675A9891E00A74A00B80903 /* PreviewView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PreviewView.swift; path = ../../Source/PreviewView.swift; sourceTree = ""; }; + 1675A98A1E00A74A00B80903 /* SwiftyCamButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwiftyCamButton.swift; path = ../../Source/SwiftyCamButton.swift; sourceTree = ""; }; + 1675A98B1E00A74A00B80903 /* SwiftyCamViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwiftyCamViewController.swift; path = ../../Source/SwiftyCamViewController.swift; sourceTree = ""; }; + 1675A98C1E00A74A00B80903 /* SwiftyCamViewControllerDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SwiftyCamViewControllerDelegate.swift; path = ../../Source/SwiftyCamViewControllerDelegate.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1675A96F1E00A68300B80903 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 1675A9691E00A68300B80903 = { + isa = PBXGroup; + children = ( + 1675A9741E00A68300B80903 /* DemoSwiftyCam */, + 1675A9731E00A68300B80903 /* Products */, + ); + sourceTree = ""; + }; + 1675A9731E00A68300B80903 /* Products */ = { + isa = PBXGroup; + children = ( + 1675A9721E00A68300B80903 /* DemoSwiftyCam.app */, + ); + name = Products; + sourceTree = ""; + }; + 1675A9741E00A68300B80903 /* DemoSwiftyCam */ = { + isa = PBXGroup; + children = ( + 1675A9911E00A74F00B80903 /* Source */, + 1675A9751E00A68300B80903 /* AppDelegate.swift */, + 1675A9771E00A68300B80903 /* ViewController.swift */, + 1675A9791E00A68300B80903 /* Main.storyboard */, + 1675A97C1E00A68300B80903 /* Assets.xcassets */, + 1675A97E1E00A68300B80903 /* LaunchScreen.storyboard */, + 1675A9811E00A68300B80903 /* Info.plist */, + ); + path = DemoSwiftyCam; + sourceTree = ""; + }; + 1675A9911E00A74F00B80903 /* Source */ = { + isa = PBXGroup; + children = ( + 1675A9891E00A74A00B80903 /* PreviewView.swift */, + 1675A98A1E00A74A00B80903 /* SwiftyCamButton.swift */, + 1675A98B1E00A74A00B80903 /* SwiftyCamViewController.swift */, + 1675A98C1E00A74A00B80903 /* SwiftyCamViewControllerDelegate.swift */, + ); + name = Source; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1675A9711E00A68300B80903 /* DemoSwiftyCam */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1675A9841E00A68300B80903 /* Build configuration list for PBXNativeTarget "DemoSwiftyCam" */; + buildPhases = ( + 1675A96E1E00A68300B80903 /* Sources */, + 1675A96F1E00A68300B80903 /* Frameworks */, + 1675A9701E00A68300B80903 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = DemoSwiftyCam; + productName = DemoSwiftyCam; + productReference = 1675A9721E00A68300B80903 /* DemoSwiftyCam.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 1675A96A1E00A68300B80903 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0810; + LastUpgradeCheck = 0820; + ORGANIZATIONNAME = Cappsule; + TargetAttributes = { + 1675A9711E00A68300B80903 = { + CreatedOnToolsVersion = 8.1; + DevelopmentTeam = LW28KCU8N5; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 1675A96D1E00A68300B80903 /* Build configuration list for PBXProject "DemoSwiftyCam" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 1675A9691E00A68300B80903; + productRefGroup = 1675A9731E00A68300B80903 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 1675A9711E00A68300B80903 /* DemoSwiftyCam */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 1675A9701E00A68300B80903 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1675A9801E00A68300B80903 /* LaunchScreen.storyboard in Resources */, + 1675A97D1E00A68300B80903 /* Assets.xcassets in Resources */, + 1675A97B1E00A68300B80903 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 1675A96E1E00A68300B80903 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1675A9781E00A68300B80903 /* ViewController.swift in Sources */, + 1675A9901E00A74A00B80903 /* SwiftyCamViewControllerDelegate.swift in Sources */, + 1675A9761E00A68300B80903 /* AppDelegate.swift in Sources */, + 1675A98F1E00A74A00B80903 /* SwiftyCamViewController.swift in Sources */, + 1675A98D1E00A74A00B80903 /* PreviewView.swift in Sources */, + 1675A98E1E00A74A00B80903 /* SwiftyCamButton.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 1675A9791E00A68300B80903 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 1675A97A1E00A68300B80903 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 1675A97E1E00A68300B80903 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 1675A97F1E00A68300B80903 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 1675A9821E00A68300B80903 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.1; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 1675A9831E00A68300B80903 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_SUSPICIOUS_MOVES = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.1; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 1675A9851E00A68300B80903 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = LW28KCU8N5; + INFOPLIST_FILE = DemoSwiftyCam/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.Cappsule.DemoSwiftyCam; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + }; + name = Debug; + }; + 1675A9861E00A68300B80903 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = LW28KCU8N5; + INFOPLIST_FILE = DemoSwiftyCam/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.Cappsule.DemoSwiftyCam; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1675A96D1E00A68300B80903 /* Build configuration list for PBXProject "DemoSwiftyCam" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1675A9821E00A68300B80903 /* Debug */, + 1675A9831E00A68300B80903 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1675A9841E00A68300B80903 /* Build configuration list for PBXNativeTarget "DemoSwiftyCam" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1675A9851E00A68300B80903 /* Debug */, + 1675A9861E00A68300B80903 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 1675A96A1E00A68300B80903 /* Project object */; +} diff --git a/DemoSwiftyCam/DemoSwiftyCam.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/DemoSwiftyCam/DemoSwiftyCam.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..7cf9cd3 --- /dev/null +++ b/DemoSwiftyCam/DemoSwiftyCam.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/DemoSwiftyCam/DemoSwiftyCam.xcodeproj/project.xcworkspace/xcuserdata/DARKPR0.xcuserdatad/UserInterfaceState.xcuserstate b/DemoSwiftyCam/DemoSwiftyCam.xcodeproj/project.xcworkspace/xcuserdata/DARKPR0.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..26c4434d3ade4da7ae7bc5b1770c3f91faf6000f GIT binary patch literal 19941 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DemoSwiftyCam/DemoSwiftyCam.xcodeproj/xcuserdata/DARKPR0.xcuserdatad/xcschemes/xcschememanagement.plist b/DemoSwiftyCam/DemoSwiftyCam.xcodeproj/xcuserdata/DARKPR0.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..7aa4b12 --- /dev/null +++ b/DemoSwiftyCam/DemoSwiftyCam.xcodeproj/xcuserdata/DARKPR0.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + DemoSwiftyCam.xcscheme + + orderHint + 0 + + + SuppressBuildableAutocreation + + 1675A9711E00A68300B80903 + + primary + + + + + diff --git a/DemoSwiftyCam/DemoSwiftyCam/AppDelegate.swift b/DemoSwiftyCam/DemoSwiftyCam/AppDelegate.swift new file mode 100644 index 0000000..dfee801 --- /dev/null +++ b/DemoSwiftyCam/DemoSwiftyCam/AppDelegate.swift @@ -0,0 +1,54 @@ +/*Copyright (c) 2016, Andrew Walz. + + Redistribution and use in source and binary forms, with or without modification,are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + func applicationWillResignActive(_ application: UIApplication) { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. + } + + func applicationDidEnterBackground(_ application: UIApplication) { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } + + func applicationWillEnterForeground(_ application: UIApplication) { + // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. + } + + func applicationDidBecomeActive(_ application: UIApplication) { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } + + func applicationWillTerminate(_ application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } + + +} + diff --git a/DemoSwiftyCam/DemoSwiftyCam/Assets.xcassets/AppIcon.appiconset/Contents.json b/DemoSwiftyCam/DemoSwiftyCam/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..b8236c6 --- /dev/null +++ b/DemoSwiftyCam/DemoSwiftyCam/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,48 @@ +{ + "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", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/DemoSwiftyCam/DemoSwiftyCam/Assets.xcassets/Camera.imageset/Camera@2x.png b/DemoSwiftyCam/DemoSwiftyCam/Assets.xcassets/Camera.imageset/Camera@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..b98c6d8faab24295c0258ddc9cac6b381e777f0c GIT binary patch literal 7324 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 D{^s~Z literal 0 HcmV?d00001 diff --git a/DemoSwiftyCam/DemoSwiftyCam/Assets.xcassets/Flash.imageset/flash@2x.png b/DemoSwiftyCam/DemoSwiftyCam/Assets.xcassets/Flash.imageset/flash@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..3429e90a94bcf0ca70f4578e79f03405cf653ba9 GIT binary patch literal 1380 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 vwtpAy9+ZDeDrcm9&2-PI + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DemoSwiftyCam/DemoSwiftyCam/Base.lproj/Main.storyboard b/DemoSwiftyCam/DemoSwiftyCam/Base.lproj/Main.storyboard new file mode 100644 index 0000000..9648719 --- /dev/null +++ b/DemoSwiftyCam/DemoSwiftyCam/Base.lproj/Main.storyboard @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DemoSwiftyCam/DemoSwiftyCam/Info.plist b/DemoSwiftyCam/DemoSwiftyCam/Info.plist new file mode 100644 index 0000000..c2cc98e --- /dev/null +++ b/DemoSwiftyCam/DemoSwiftyCam/Info.plist @@ -0,0 +1,42 @@ + + + + + NSMicrophoneUsageDescription + To record audio with videos + NSCameraUsageDescription + To take photos and videos + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/DemoSwiftyCam/DemoSwiftyCam/ViewController.swift b/DemoSwiftyCam/DemoSwiftyCam/ViewController.swift new file mode 100644 index 0000000..d3f1dc7 --- /dev/null +++ b/DemoSwiftyCam/DemoSwiftyCam/ViewController.swift @@ -0,0 +1,83 @@ +/*Copyright (c) 2016, Andrew Walz. + + Redistribution and use in source and binary forms, with or without modification,are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + + +import UIKit + +class ViewController: SwiftyCamViewController, SwiftyCamViewControllerDelegate { + + @IBOutlet weak var flipCameraButton: UIButton! + @IBOutlet weak var toggleFlashButton: UIButton! + + override func viewDidLoad() { + super.viewDidLoad() + cameraDelegate = self + kMaximumVideoDuration = 10.0 + tapToFocus = true + pinchToZoom = true + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + let button : SwiftyCamButton = SwiftyCamButton(frame: CGRect(x: (self.view.frame.width / 2) - 35, y: self.view.frame.height - 85, width: 70, height: 70)) + button.delegate = self + button.setImage(UIImage(named: "Camera"), for: UIControlState()) + self.view.addSubview(button) + self.view.bringSubview(toFront: flipCameraButton) + self.view.bringSubview(toFront: toggleFlashButton) + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } + + func SwiftyCamDidTakePhoto(_ photo: UIImage) { + print(photo) + } + + func SwiftyCamDidBeginRecordingVideo() { + print("Did Begin Recording") + } + + func SwiftyCamDidFinishRecordingVideo() { + print("Did finish Recording") + } + + func SwiftyCamDidFinishProcessingVideoAt(_ url: String) { + print(url) + } + + func SwiftyCamDidFocusAtPoint(focusPoint: CGPoint) { + print(focusPoint) + } + + func SwiftyCamDidChangeZoomLevel(zoomLevel: CGFloat) { + print(zoomLevel) + } + + func SwiftyCamDidSwitchCameras(camera: SwiftyCamViewController.CameraSelection) { + print(camera) + } + + @IBAction func cameraSwitchAction(_ sender: Any) { + switchCamera() + } + + @IBAction func toggleFlashAction(_ sender: Any) { + toggleFlash() + } +} + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8ed1baa --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2016, Andrew Walz. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, +OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..4658a2d --- /dev/null +++ b/README.md @@ -0,0 +1,205 @@ +# SwiftyCam + + +[![Version](https://img.shields.io/cocoapods/v/SwiftyCam.svg?style=flat)](http://cocoapods.org/pods/SwiftyCam) +[![License](https://img.shields.io/cocoapods/l/SwiftyCam.svg?style=flat)](http://cocoapods.org/pods/SwiftyCam) +[![Platform](https://img.shields.io/cocoapods/p/SwiftyCam.svg?style=flat)](http://cocoapods.org/pods/SwiftyCam) + +## Overview + +SwiftyCam is a a simple, clean iOS Camera framework for easy photo and video capture. SwiftyCam allows users to capture both photos and videos from the same session with very little configuration. + +Configuring a Camera View Controller in AVFoundation can be tedious and time consuming. SwiftyCam is a drop in View Controller which gives complete control of the AVSession. + +## Requirements + +* iOS 8.0+ +* Swift 3.0+ + +## License + +SwiftyCam is available under the BSD license. See the LICENSE file for more info. + + +## Installation + +### Cocoapods: + +SwiftyCam is available through [CocoaPods](http://cocoapods.org). To install +it, simply add the following line to your Podfile: + +```ruby +pod "SwiftyCam" +``` + +### Manual Installation: + +Simply copy the contents of the Source folder into your project. + +## Usage + +Using SwiftyCam is very simple. + +### Prerequisites: + +As of iOS 10, Apple requires the additon of the NSCameraUsageDescription and NSMicrophoneUsageDescription strings to the info.plist of your application. Example: + + NSCameraUsageDescription + To Take Photos and Video + NSMicrophoneUsageDescription + To Record Audio With Video + +### Getting Started: + +Add the import statement to the View Controller you ware working in: + + import SwiftyCam + +SwiftyCam is a drop-in convenience framework. To create a Camera instance, create a new UIViewController subclass. Replace the UIViewController subclass declaration with SwiftyCamViewController: + + class MyCameraViewController : SwiftyCamViewController + +That is all that is required to setup the AVSession for photo and video capture. SwiftyCam will prompt the user for permission to use the camera/microphone, and configure both the device inputs and outputs. + +## Capture + +### SwiftyCamButton: + +SwiftyCam comes with a very convenient method of capturing media. SwiftyCamButton uses gesture recognizers to take either photos of videos. + +A single tap of the SwiftyCam button triggers a photo capture. + +A long press/tap and hold gesture initiates a video capture. Releasing the button ends the video recoring. + +To use a SwiftyCamButton, simply create one and assign the delegate to your SwiftyCamViewController: + + let captureButton = SwiftyCamButton(frame: buttonFrame) + captureButton.delegate = self + +### Manual: + +Capturing media with SwiftyCam is very simple. To capture a photo, simply call the takePhoto function: + + takePhoto() + +Capturing Video is just as easy. To begin recording video, call the startVideoRecording function: + + startVideoRecording() + +To end the capture of a video, call the endVideoRecordingFunction: + + endVideoRecording() + +###Delegate + +In order to acquire the photos and videos taken by either the SwiftyCamButton or manually, you must implement the SwiftyCamViewControllerDelegate and set the delegate to your view controller instance: + + class MyCameraViewController : SwiftyCamViewController, SwiftyCamViewControllerDelegate + +and in your viewDidLoad, assign the cameraDelegate to self: + + self.cameraDelegate = self + +####Delegate methods: + +**SwiftyCamDidTakePhoto(_ photo:UIImage)** - Return a UIImage captured from the AVSession + +**SwiftyCamDidBeginRecordingVideo()** - Called when the capture session begins recording a video + +**SwiftyCamDidFinishRecordingVideo()** - Called when the capture session has finished recording a video and has begun processing + +**SwiftyCamDidFinishProcessingVideoAt(_ url: String)** - Called when the capture session finished processing the video returns the location on disk the video is stored. This will always be in the temorary folder of the device. + +**SwiftyCamDidSwitchCameras(camera: SwiftyCamViewController.CameraSelection)** - Called when the user has initiated a switch in camera orientations. + +**SwiftyCamDidFocusAtPoint(focusPoint: CGPoint)** - Returns the point on the preview where a tap to focus was initiated (Will only be called if *tapToFocus* is set to *true*) + +**SwiftyCamDidChangeZoomLevel(zoomLevel: CGFloat)** - Returns the current zoom level of the preview layer after a pinch to zoom has been initiated. Will be called several times (Will only be called if *pinchToZoom* is set to *true*) + +**SwiftyCamDidFailCameraPermissionSettings()** - Called during SwiftyCamViewController launch if the user has denied permission to access either the camera of microphone (Will only be called if *promptToAppPrivacySettings* is set to *false*) + +## Flash + +The flash(torch) can be enabled on the device by calling: + + toggleFlash() + +The flash will only be enabled if the current camera is the rear camera and will automatically if the camera switches or the View Controller is dismissed. + +## Switching Camera + +SwiftyCam supports capture from both the front and back cameras. To switch cameras, call the function: + + switchCamera() + +Tap-to-focus, pinch-to-zoom and camera flash are not supported when the front facing camera is selected. *Switching video while video is being recorded is not currently supported* + +##Configuration + +SwiftyCam has several options for configurating the functionality of the capture: + +### Video Quality + +Video quality can be set by the **videoQuality** property of SwiftyCamViewController. The choices available correspond to the matching **AVCaptureSessionPreset**: + +* **.high** (AVCapturePresetHigh) +* **.medium** (AVCapturePresetMedium) +* **.low** (AVCapturePresetLow) +* **.resolution352x288** (AVCaptureSessionPreset352x288) +* **.resolution640x480** (AVCaptureSessionPreset640x480) +* **.resolution1280x720** (AVCaptureSessionPreset1280x720) +* **.resolution1920x1080** (AVCaptureSessionPreset1920x1080) +* **.resolution3840x2160** (AVCaptureSessionPreset3840x2160) +* **.iframe960x540** (AVCaptureSessionPresetiFrame960x540) +* **.iframe1280x720** (AVCaptureSessionPresetiFrame1280x720) + +The default value is **.resolution1920x1080** + +### Maximum Video Duration + +If using a SwiftyCamButton, you can set a maximum video duration for the length of video. The video recording will me automatically stopped once the time limit has been reached and the delegate method **SwiftyCamDidFinishRecordingVideo** will be called. To set this value, simply change the **kMaximumVideoDuration** value: + + kMaximumVideoDuration = 10.0 + +A value of **0.0** will allow for unlimited video recording via the SwiftyCamButton. The default value is **0.0**. + +## Camera Zoom + +SwiftyCam supports digital zoom of the camera session via pinch gestures. The gestures work similar to the default iOS app and will zoom to the maximum supported zoom level. Camera zoom is only supported on the rear facing camera. AVFoundation does not currently support front facing camera zoom. To disable this feature, change the **pinchToZoom** property: + + pinchToZoom = false + +By default, **pinchToZoom** is enabled. + +## Camera Focus + +SwiftyCam, by default, support tap to focus on the video preview. SwiftyCam will set the focus and exposure levels of the session to the tapped point. Autofocus and autoexposure will be resumed once SwiftyCam detects significant movement from the tapped point. To disable this feature, change the **tapToFocus** property: + + tapToFocus = false + +By default, **tapToFocus** is enabled. If you wish to show a on screen animation when a tap to focus is initiated, you can use the **SwiftyCamDidFocusAtPoint(focusPoint:)** to get the coordinates of tap and provide your own tap animation + +## Privacy + +When a user firsts launch SwiftyCamViewController, they will be prompted for permission for access to the cameras and microphones. By default, if a user declines access to the hardware, SwiftyCam will provide a prompt to the App privacy settings inside the iOS settings application. If you wish to change this behaviour, the **promptToAppPrivacySettings** property can be modified: + + promptToAppPrivacySettings = false + +Instead of prompting the user to the settings application, the delegate method **SwiftyCamDidFailCameraPermissionSettings** will be called and will have to be manually handled. + +## Miscellaneous + +Other properties: + +* **isCameraFlashOn** - Bool +* **isVideoRecording** - Bool +* **isSessionRunning** - Bool +* **currentCamera** - CameraSelection + + +### Contact + +If you have any questions, requests, or enhancements, feel free to submit a pull request, create an issue, or contact me in person: + +**Andrew Walz** +**andrewjwalz@gmail.com** diff --git a/Source/PreviewView.swift b/Source/PreviewView.swift new file mode 100644 index 0000000..7d4c52d --- /dev/null +++ b/Source/PreviewView.swift @@ -0,0 +1,49 @@ +/*Copyright (c) 2016, Andrew Walz. + +Redistribution and use in source and binary forms, with or without modification,are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + + +import UIKit +import AVFoundation + +class PreviewView: UIView { + + override init(frame: CGRect) { + super.init(frame: frame) + self.backgroundColor = UIColor.black + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + var videoPreviewLayer: AVCaptureVideoPreviewLayer { + return layer as! AVCaptureVideoPreviewLayer + } + + var session: AVCaptureSession? { + get { + return videoPreviewLayer.session + } + set { + videoPreviewLayer.session = newValue + } + } + + // MARK: UIView + + override class var layerClass : AnyClass { + return AVCaptureVideoPreviewLayer.self + } +} diff --git a/Source/SwiftyCamButton.swift b/Source/SwiftyCamButton.swift new file mode 100644 index 0000000..b8bf121 --- /dev/null +++ b/Source/SwiftyCamButton.swift @@ -0,0 +1,82 @@ +/*Copyright (c) 2016, Andrew Walz. + + Redistribution and use in source and binary forms, with or without modification,are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + + +import UIKit + +public protocol SwiftyCamButtonDelegate { + func buttonWasTapped() + func buttonDidBeginLongPress() + func buttonDidEndLongPress() + func longPressDidReachMaximumDuration() + func setMaxiumVideoDuration() -> Double +} + + +open class SwiftyCamButton: UIButton { + + public var delegate: SwiftyCamButtonDelegate? + + fileprivate var timer : Timer? + + override public init(frame: CGRect) { + super.init(frame: frame) + createGestureRecognizers() + } + + required public init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + createGestureRecognizers() + } + + @objc fileprivate func Tap() { + self.delegate?.buttonWasTapped() + } + + @objc fileprivate func LongPress(_ sender:UILongPressGestureRecognizer!) { + if (sender.state == UIGestureRecognizerState.ended) { + invalidateTimer() + self.delegate?.buttonDidEndLongPress() + } else if (sender.state == UIGestureRecognizerState.began) { + self.delegate?.buttonDidBeginLongPress() + startTimer() + } + } + + @objc fileprivate func timerFinished() { + invalidateTimer() + self.delegate?.longPressDidReachMaximumDuration() + } + + fileprivate func startTimer() { + if let duration = delegate?.setMaxiumVideoDuration() { + if duration != 0.0 && duration > 0.0 { + timer = Timer.scheduledTimer(timeInterval: duration, target: self, selector: #selector(SwiftyCamButton.timerFinished), userInfo: nil, repeats: false) + } + } + } + + fileprivate func invalidateTimer() { + timer?.invalidate() + timer = nil + } + + fileprivate func createGestureRecognizers() { + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(SwiftyCamButton.Tap)) + let longGesture = UILongPressGestureRecognizer(target: self, action: #selector(SwiftyCamButton.LongPress)) + self.addGestureRecognizer(tapGesture) + self.addGestureRecognizer(longGesture) + } +} diff --git a/Source/SwiftyCamViewController.swift b/Source/SwiftyCamViewController.swift new file mode 100644 index 0000000..93ad9d9 --- /dev/null +++ b/Source/SwiftyCamViewController.swift @@ -0,0 +1,536 @@ +/*Copyright (c) 2016, Andrew Walz. + + Redistribution and use in source and binary forms, with or without modification,are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + + +import UIKit +import AVFoundation + +open class SwiftyCamViewController: UIViewController { + + public enum CameraSelection { + case rear + case front + } + + public enum VideoQuality { + case high + case medium + case low + case resolution352x288 + case resolution640x480 + case resolution1280x720 + case resolution1920x1080 + case resolution3840x2160 + case iframe960x540 + case iframe1280x720 + } + + fileprivate enum SessionSetupResult { + case success + case notAuthorized + case configurationFailed + } + + public var cameraDelegate: SwiftyCamViewControllerDelegate? + + public var kMaximumVideoDuration : Double = 0.0 + public var videoQuality : VideoQuality = .resolution1920x1080 + public var pinchToZoom = true + public var tapToFocus = true + public var promptToAppPrivacySettings = true + private(set) public var isCameraFlashOn = false + private(set) public var isVideRecording = false + private(set) public var isSessionRunning = false + private(set) public var currentCamera = CameraSelection.rear + fileprivate let session = AVCaptureSession() + fileprivate let sessionQueue = DispatchQueue(label: "session queue", attributes: []) + fileprivate var zoomScale = CGFloat(1.0) + fileprivate var beginZoomScale = CGFloat(1.0) + fileprivate var setupResult = SessionSetupResult.success + fileprivate var backgroundRecordingID : UIBackgroundTaskIdentifier? = nil + fileprivate var videoDeviceInput : AVCaptureDeviceInput! + fileprivate var movieFileOutput : AVCaptureMovieFileOutput? + fileprivate var photoFileOutput : AVCaptureStillImageOutput? + fileprivate var videoDevice : AVCaptureDevice? + fileprivate var previewLayer : PreviewView! + + + + override open func viewDidLoad() { + super.viewDidLoad() + previewLayer = PreviewView(frame: self.view.frame) + addGestureRecognizers(toView: previewLayer) + self.view.addSubview(previewLayer) + previewLayer.session = session + + switch AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo){ + case .authorized: + break + case .notDetermined: + sessionQueue.suspend() + AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo, completionHandler: { [unowned self] granted in + if !granted { + self.setupResult = .notAuthorized + } + self.sessionQueue.resume() + }) + default: + setupResult = .notAuthorized + } + sessionQueue.async { [unowned self] in + self.configureSession() + } + } + + override open func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + sessionQueue.async { + switch self.setupResult { + case .success: + self.session.startRunning() + self.isSessionRunning = self.session.isRunning + case .notAuthorized: + self.promptToAppSettings() + case .configurationFailed: + DispatchQueue.main.async(execute: { [unowned self] in + let message = NSLocalizedString("Unable to capture media", comment: "Alert message when something goes wrong during capture session configuration") + let alertController = UIAlertController(title: "AVCam", message: message, preferredStyle: .alert) + alertController.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "Alert OK button"), style: .cancel, handler: nil)) + + self.present(alertController, animated: true, completion: nil) + }) + } + } + } + + override open func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + if self.isSessionRunning == true { + self.session.stopRunning() + self.isSessionRunning = false + } + disableFlash() + } + + func takePhoto() { + if let videoConnection = photoFileOutput?.connection(withMediaType: AVMediaTypeVideo) { + videoConnection.videoOrientation = AVCaptureVideoOrientation.portrait + photoFileOutput?.captureStillImageAsynchronously(from: videoConnection, completionHandler: {(sampleBuffer, error) in + if (sampleBuffer != nil) { + let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer) + let image = self.processPhoto(imageData!) + self.cameraDelegate?.SwiftyCamDidTakePhoto(image) + } + }) + } + } + + func startVideoRecording() { + guard let movieFileOutput = self.movieFileOutput else { + return + } + + let videoPreviewLayerOrientation = previewLayer!.videoPreviewLayer.connection.videoOrientation + + sessionQueue.async { [unowned self] in + if !movieFileOutput.isRecording { + if UIDevice.current.isMultitaskingSupported { + self.backgroundRecordingID = UIApplication.shared.beginBackgroundTask(expirationHandler: nil) + } + + // Update the orientation on the movie file output video connection before starting recording. + let movieFileOutputConnection = self.movieFileOutput?.connection(withMediaType: AVMediaTypeVideo) + + + //flip video output if front facing camera is selected + if self.currentCamera == .front { + movieFileOutputConnection?.isVideoMirrored = true + } + movieFileOutputConnection?.videoOrientation = videoPreviewLayerOrientation + + // Start recording to a temporary file. + let outputFileName = UUID().uuidString + let outputFilePath = (NSTemporaryDirectory() as NSString).appendingPathComponent((outputFileName as NSString).appendingPathExtension("mov")!) + movieFileOutput.startRecording(toOutputFileURL: URL(fileURLWithPath: outputFilePath), recordingDelegate: self) + self.isVideRecording = true + self.cameraDelegate?.SwiftyCamDidBeginRecordingVideo() + } + else { + movieFileOutput.stopRecording() + } + } + } + + func endVideoRecording() { + if self.movieFileOutput?.isRecording == true { + self.isVideRecording = false + movieFileOutput!.stopRecording() + self.cameraDelegate?.SwiftyCamDidFinishRecordingVideo() + } + } + + func switchCamera() { + if isVideRecording == true { + print("[SwiftyCam]: Switching between cameras while recoring video is not supported") + return + } + switch currentCamera { + case .front: + self.currentCamera = .rear + case .rear: + self.currentCamera = .front + } + + sessionQueue.async { [unowned self] in + let currentInputs : [AVCaptureInput] = self.session.inputs as! [AVCaptureInput] + let currentOutputs : [AVCaptureOutput] = self.session.outputs as! [AVCaptureOutput] + + for input in currentInputs { + self.session.removeInput(input) + } + for output in currentOutputs { + self.session.removeOutput(output) + } + + self.configureSession() + self.cameraDelegate?.SwiftyCamDidSwitchCameras(camera: self.currentCamera) + } + disableFlash() + } + + func toggleFlash() { + guard self.currentCamera == .rear else { + return + } + let device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo) + if (device?.hasTorch)! { + do { + try device?.lockForConfiguration() + if (device?.torchMode == AVCaptureTorchMode.on) { + device?.torchMode = AVCaptureTorchMode.off + self.isCameraFlashOn = false + } else { + do { + try device?.setTorchModeOnWithLevel(1.0) + self.isCameraFlashOn = true + } catch { + print("[SwiftyCam]: \(error)") + } + } + device?.unlockForConfiguration() + } catch { + print("[SwiftyCam]: \(error)") + } + } + } + + override open func touchesBegan(_ touches: Set, with event: UIEvent?) { + guard tapToFocus == true, currentCamera == .rear else { + return + } + let screenSize = previewLayer!.bounds.size + if let touchPoint = touches.first { + let x = touchPoint.location(in: previewLayer!).y / screenSize.height + let y = 1.0 - touchPoint.location(in: previewLayer!).x / screenSize.width + let focusPoint = CGPoint(x: x, y: y) + + if let device = videoDevice { + do { + try device.lockForConfiguration() + + device.focusPointOfInterest = focusPoint + //device.focusMode = .continuousAutoFocus + device.focusMode = .autoFocus + //device.focusMode = .locked + device.exposurePointOfInterest = focusPoint + device.exposureMode = AVCaptureExposureMode.continuousAutoExposure + device.unlockForConfiguration() + self.cameraDelegate?.SwiftyCamDidFocusAtPoint(focusPoint: touchPoint.location(in: previewLayer)) + } + catch { + // just ignore + } + } + } + } + + /**************************************** Private Functions ****************************************/ + + fileprivate func configureSession() { + guard setupResult == .success else { + return + } + session.beginConfiguration() + + addVideoInput() + addAudioInput() + configureVideoOutput() + configurePhotoOutput() + + session.commitConfiguration() + } + + fileprivate func addVideoInput() { + switch currentCamera { + case .front: + videoDevice = SwiftyCamViewController.deviceWithMediaType(AVMediaTypeVideo, preferringPosition: .front) + case .rear: + videoDevice = SwiftyCamViewController.deviceWithMediaType(AVMediaTypeVideo, preferringPosition: .back) + } + + if let device = videoDevice { + do { + try device.lockForConfiguration() + if device.isFocusModeSupported(.continuousAutoFocus) { + device.focusMode = .continuousAutoFocus + if device.isSmoothAutoFocusSupported { + device.isSmoothAutoFocusEnabled = true + } + } + + if device.isExposureModeSupported(.continuousAutoExposure) { + device.exposureMode = .continuousAutoExposure + } + + if device.isWhiteBalanceModeSupported(.continuousAutoWhiteBalance) { + device.whiteBalanceMode = .continuousAutoWhiteBalance + } + + device.unlockForConfiguration() + } catch { + print("[SwiftyCam]: Error locking configuration") + } + } + + do { + let videoDeviceInput = try AVCaptureDeviceInput(device: videoDevice) + + if session.canAddInput(videoDeviceInput) { + session.addInput(videoDeviceInput) + self.videoDeviceInput = videoDeviceInput + } else { + print("[SwiftyCam]: Could not add video device input to the session") + setupResult = .configurationFailed + session.commitConfiguration() + return + } + } catch { + print("[SwiftyCam]: Could not create video device input: \(error)") + setupResult = .configurationFailed + return + } + } + + fileprivate func addAudioInput() { + do { + let audioDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeAudio) + let audioDeviceInput = try AVCaptureDeviceInput(device: audioDevice) + + if session.canAddInput(audioDeviceInput) { + session.addInput(audioDeviceInput) + } + else { + print("[SwiftyCam]: Could not add audio device input to the session") + } + } + catch { + print("[SwiftyCam]: Could not create audio device input: \(error)") + } + } + + fileprivate func configureVideoOutput() { + let movieFileOutput = AVCaptureMovieFileOutput() + + if self.session.canAddOutput(movieFileOutput) { + self.session.addOutput(movieFileOutput) + self.session.sessionPreset = videoInputPresetFromVideoQuality(quality: videoQuality) + print(videoInputPresetFromVideoQuality(quality: videoQuality)) + if let connection = movieFileOutput.connection(withMediaType: AVMediaTypeVideo) { + if connection.isVideoStabilizationSupported { + connection.preferredVideoStabilizationMode = .auto + } + } + self.movieFileOutput = movieFileOutput + } + } + + fileprivate func configurePhotoOutput() { + let photoFileOutput = AVCaptureStillImageOutput() + + if self.session.canAddOutput(photoFileOutput) { + photoFileOutput.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG] + self.session.addOutput(photoFileOutput) + self.photoFileOutput = photoFileOutput + } + } + + fileprivate func processPhoto(_ imageData: Data) -> UIImage { + let dataProvider = CGDataProvider(data: imageData as CFData) + let cgImageRef = CGImage(jpegDataProviderSource: dataProvider!, decode: nil, shouldInterpolate: true, intent: CGColorRenderingIntent.defaultIntent) + + var image: UIImage! + + switch self.currentCamera { + case .front: + image = UIImage(cgImage: cgImageRef!, scale: 1.0, orientation: .rightMirrored) + case .rear: + image = UIImage(cgImage: cgImageRef!, scale: 1.0, orientation: .right) + } + return image + } + + @objc fileprivate func zoomGesture(pinch: UIPinchGestureRecognizer) { + guard pinchToZoom == true else { + return + } + do { + let captureDevice = AVCaptureDevice.devices().first as? AVCaptureDevice + try captureDevice?.lockForConfiguration() + + zoomScale = max(1.0, min(beginZoomScale * pinch.scale, captureDevice!.activeFormat.videoMaxZoomFactor)) + + captureDevice?.videoZoomFactor = zoomScale + + self.cameraDelegate?.SwiftyCamDidChangeZoomLevel(zoomLevel: zoomScale) + + captureDevice?.unlockForConfiguration() + + } catch { + print("[SwiftyCam]: Error locking configuration") + } + } + + fileprivate func addGestureRecognizers(toView: UIView) { + let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(zoomGesture(pinch:))) + pinchGesture.delegate = self + toView.addGestureRecognizer(pinchGesture) + } + + fileprivate func promptToAppSettings() { + guard promptToAppPrivacySettings == true else { + self.cameraDelegate?.SwiftyCamDidFailCameraPermissionSettings() + return + } + DispatchQueue.main.async(execute: { [unowned self] in + let message = NSLocalizedString("AVCam doesn't have permission to use the camera, please change privacy settings", comment: "Alert message when the user has denied access to the camera") + let alertController = UIAlertController(title: "AVCam", message: message, preferredStyle: .alert) + alertController.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "Alert OK button"), style: .cancel, handler: nil)) + alertController.addAction(UIAlertAction(title: NSLocalizedString("Settings", comment: "Alert button to open Settings"), style: .default, handler: { action in + if #available(iOS 10.0, *) { + UIApplication.shared.openURL(URL(string: UIApplicationOpenSettingsURLString)!) + } else { + if let appSettings = URL(string: UIApplicationOpenSettingsURLString) { + UIApplication.shared.openURL(appSettings) + } + } + })) + self.present(alertController, animated: true, completion: nil) + }) + } + + fileprivate func videoInputPresetFromVideoQuality(quality: VideoQuality) -> String { + switch quality { + case .high: return AVCaptureSessionPresetHigh + case .medium: return AVCaptureSessionPresetMedium + case .low: return AVCaptureSessionPresetLow + case .resolution352x288: return AVCaptureSessionPreset352x288 + case .resolution640x480: return AVCaptureSessionPreset640x480 + case .resolution1280x720: return AVCaptureSessionPreset1280x720 + case .resolution1920x1080: return AVCaptureSessionPreset1920x1080 + case .iframe960x540: return AVCaptureSessionPresetiFrame960x540 + case .iframe1280x720: return AVCaptureSessionPresetiFrame1280x720 + case .resolution3840x2160: + if #available(iOS 9.0, *) { + return AVCaptureSessionPreset3840x2160 + } + else { + print("[SwiftyCam]: Resolution 3840x2160 not supported") + return AVCaptureSessionPresetPhoto + } + } + } + + fileprivate class func deviceWithMediaType(_ mediaType: String, preferringPosition position: AVCaptureDevicePosition) -> AVCaptureDevice? { + if let devices = AVCaptureDevice.devices(withMediaType: mediaType) as? [AVCaptureDevice] { + return devices.filter({ $0.position == position }).first + } + return nil + } + + fileprivate func enableFlash() { + if self.isCameraFlashOn == false { + toggleFlash() + } + } + + fileprivate func disableFlash() { + if self.isCameraFlashOn == true { + toggleFlash() + } + } +} + +extension SwiftyCamViewController : SwiftyCamButtonDelegate { + + public func setMaxiumVideoDuration() -> Double { + return kMaximumVideoDuration + } + + public func buttonWasTapped() { + takePhoto() + } + + public func buttonDidBeginLongPress() { + startVideoRecording() + } + + public func buttonDidEndLongPress() { + endVideoRecording() + } + + public func longPressDidReachMaximumDuration() { + endVideoRecording() + } +} + +extension SwiftyCamViewController : AVCaptureFileOutputRecordingDelegate { + public func capture(_ captureOutput: AVCaptureFileOutput!, didFinishRecordingToOutputFileAt outputFileURL: URL!, fromConnections connections: [Any]!, error: Error!) { + if let currentBackgroundRecordingID = backgroundRecordingID { + backgroundRecordingID = UIBackgroundTaskInvalid + + if currentBackgroundRecordingID != UIBackgroundTaskInvalid { + UIApplication.shared.endBackgroundTask(currentBackgroundRecordingID) + } + } + if error != nil { + print("[SwiftyCam]: Movie file finishing error: \(error)") + } else { + self.cameraDelegate?.SwiftyCamDidFinishProcessingVideoAt(outputFileURL.path) + } + } +} + +extension SwiftyCamViewController : UIGestureRecognizerDelegate { + public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { + if gestureRecognizer.isKind(of: UIPinchGestureRecognizer.self) { + beginZoomScale = zoomScale; + } + return true + } +} + + + + diff --git a/Source/SwiftyCamViewControllerDelegate.swift b/Source/SwiftyCamViewControllerDelegate.swift new file mode 100644 index 0000000..525f89b --- /dev/null +++ b/Source/SwiftyCamViewControllerDelegate.swift @@ -0,0 +1,65 @@ +/*Copyright (c) 2016, Andrew Walz. + + Redistribution and use in source and binary forms, with or without modification,are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + + +import UIKit + +public protocol SwiftyCamViewControllerDelegate { + func SwiftyCamDidTakePhoto(_ photo:UIImage) + func SwiftyCamDidBeginRecordingVideo() + func SwiftyCamDidFinishRecordingVideo() + func SwiftyCamDidFinishProcessingVideoAt(_ url: String) + func SwiftyCamDidSwitchCameras(camera: SwiftyCamViewController.CameraSelection) + func SwiftyCamDidFocusAtPoint(focusPoint: CGPoint) + func SwiftyCamDidChangeZoomLevel(zoomLevel: CGFloat) + func SwiftyCamDidFailCameraPermissionSettings() +} + +public extension SwiftyCamViewControllerDelegate { + func SwiftyCamDidTakePhoto(_ photo:UIImage) { + // Optional + } + + func SwiftyCamDidBeginRecordingVideo() { + // Optional + } + + func SwiftyCamDidFinishRecordingVideo() { + // Optional + } + + func SwiftyCamDidFinishProcessingVideoAt(_ url: String) { + // Optional + } + + func SwiftyCamDidSwitchCameras(camera: SwiftyCamViewController.CameraSelection) { + // Optional + } + + func SwiftyCamDidFocusAtPoint(focusPoint: CGPoint) { + // Optional + } + + func SwiftyCamDidChangeZoomLevel(zoomLevel: CGFloat) { + // Optional + } + + func SwiftyCamDidFailCameraPermissionSettings() { + // Optional + } +} + + + diff --git a/SwiftyCam.podspec b/SwiftyCam.podspec new file mode 100644 index 0000000..25515e1 --- /dev/null +++ b/SwiftyCam.podspec @@ -0,0 +1,44 @@ +# +# Be sure to run `pod lib lint SwiftyCam.podspec' to ensure this is a +# valid spec before submitting. +# +# Any lines starting with a # are optional, but their use is encouraged +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html +# + +Pod::Spec.new do |s| + s.name = 'SwiftyCam' + s.version = '1.0.0' + s.summary = 'A Simple, Snapchat-style camera Framework written in Swift' + s.ios.deployment_target = '8.0' + + +# This description is used to generate tags and improve search results. +# * Think: What does it do? Why did you write it? What is the focus? +# * Try to keep it short, snappy and to the point. +# * Write the description between the DESC delimiters below. +# * Finally, don't worry about the indent, CocoaPods strips it! + +s.description = <<-DESC +A drop in Camera View Controller for capturing photos and videos from one AVSession. Written in Swift. + DESC + + s.homepage = 'https://github.com/Awalz/SwiftyCam' + # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2' + s.license = { :type => 'BSD', :file => 'LICENSE' } + s.author = { 'Andrew Walz' => 'andrewjwalz@gmail.com' } + s.source = { :git => 'https://github.com/Awalz/SwiftyCam.git', :tag => s.version.to_s } + # s.social_media_url = 'https://twitter.com/' + + s.ios.deployment_target = '8.0' + + s.source_files = 'Source/**/*' + + # s.resource_bundles = { + # 'SwiftyCam' => ['SwiftyCam/Assets/*.png'] + # } + + # s.public_header_files = 'Pod/Classes/**/*.h' + # s.frameworks = 'UIKit', 'MapKit' + # s.dependency 'AFNetworking', '~> 2.3' +end