Merge pull request #190 from Awalz/Swift4.2

Swift 4.2 to master
This commit is contained in:
Jon Andersen 2018-11-21 10:29:16 -05:00 committed by GitHub
commit 88779afb79
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 562 additions and 288 deletions

View file

@ -7,6 +7,8 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
056AAB091F97CB1700F6A978 /* Orientation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 056AAB081F97CB1700F6A978 /* Orientation.swift */; };
056AAB0A1F97CB1E00F6A978 /* Orientation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 056AAB081F97CB1700F6A978 /* Orientation.swift */; };
05D2A9B81E80BE9700B479E9 /* SwiftyCam-iOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 05D2A9B61E80BE9700B479E9 /* SwiftyCam-iOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; 05D2A9B81E80BE9700B479E9 /* SwiftyCam-iOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 05D2A9B61E80BE9700B479E9 /* SwiftyCam-iOS.h */; settings = {ATTRIBUTES = (Public, ); }; };
05D2A9BC1E80BE9D00B479E9 /* PreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1675A9891E00A74A00B80903 /* PreviewView.swift */; }; 05D2A9BC1E80BE9D00B479E9 /* PreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1675A9891E00A74A00B80903 /* PreviewView.swift */; };
05D2A9BD1E80BE9D00B479E9 /* SwiftyCamButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1675A98A1E00A74A00B80903 /* SwiftyCamButton.swift */; }; 05D2A9BD1E80BE9D00B479E9 /* SwiftyCamButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1675A98A1E00A74A00B80903 /* SwiftyCamButton.swift */; };
@ -27,6 +29,7 @@
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
056AAB081F97CB1700F6A978 /* Orientation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Orientation.swift; path = ../../Source/Orientation.swift; sourceTree = "<group>"; };
05D2A9B41E80BE9700B479E9 /* SwiftyCam.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftyCam.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05D2A9B41E80BE9700B479E9 /* SwiftyCam.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftyCam.framework; sourceTree = BUILT_PRODUCTS_DIR; };
05D2A9B61E80BE9700B479E9 /* SwiftyCam-iOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SwiftyCam-iOS.h"; sourceTree = "<group>"; }; 05D2A9B61E80BE9700B479E9 /* SwiftyCam-iOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SwiftyCam-iOS.h"; sourceTree = "<group>"; };
05D2A9B71E80BE9700B479E9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; 05D2A9B71E80BE9700B479E9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@ -111,6 +114,7 @@
1675A9911E00A74F00B80903 /* Source */ = { 1675A9911E00A74F00B80903 /* Source */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
056AAB081F97CB1700F6A978 /* Orientation.swift */,
1675A9891E00A74A00B80903 /* PreviewView.swift */, 1675A9891E00A74A00B80903 /* PreviewView.swift */,
1675A98A1E00A74A00B80903 /* SwiftyCamButton.swift */, 1675A98A1E00A74A00B80903 /* SwiftyCamButton.swift */,
1675A98B1E00A74A00B80903 /* SwiftyCamViewController.swift */, 1675A98B1E00A74A00B80903 /* SwiftyCamViewController.swift */,
@ -175,7 +179,7 @@
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastSwiftUpdateCheck = 0810; LastSwiftUpdateCheck = 0810;
LastUpgradeCheck = 0820; LastUpgradeCheck = 1010;
ORGANIZATIONNAME = Cappsule; ORGANIZATIONNAME = Cappsule;
TargetAttributes = { TargetAttributes = {
05D2A9B31E80BE9700B479E9 = { 05D2A9B31E80BE9700B479E9 = {
@ -184,7 +188,7 @@
}; };
1675A9711E00A68300B80903 = { 1675A9711E00A68300B80903 = {
CreatedOnToolsVersion = 8.1; CreatedOnToolsVersion = 8.1;
DevelopmentTeam = DGV5BLXSF9; LastSwiftMigration = 1010;
ProvisioningStyle = Automatic; ProvisioningStyle = Automatic;
}; };
}; };
@ -234,6 +238,7 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
05D2A9BC1E80BE9D00B479E9 /* PreviewView.swift in Sources */, 05D2A9BC1E80BE9D00B479E9 /* PreviewView.swift in Sources */,
056AAB0A1F97CB1E00F6A978 /* Orientation.swift in Sources */,
05D2A9BF1E80BE9D00B479E9 /* SwiftyCamViewControllerDelegate.swift in Sources */, 05D2A9BF1E80BE9D00B479E9 /* SwiftyCamViewControllerDelegate.swift in Sources */,
05D2A9BE1E80BE9D00B479E9 /* SwiftyCamViewController.swift in Sources */, 05D2A9BE1E80BE9D00B479E9 /* SwiftyCamViewController.swift in Sources */,
05D2A9BD1E80BE9D00B479E9 /* SwiftyCamButton.swift in Sources */, 05D2A9BD1E80BE9D00B479E9 /* SwiftyCamButton.swift in Sources */,
@ -250,6 +255,7 @@
1675A9761E00A68300B80903 /* AppDelegate.swift in Sources */, 1675A9761E00A68300B80903 /* AppDelegate.swift in Sources */,
1675A98F1E00A74A00B80903 /* SwiftyCamViewController.swift in Sources */, 1675A98F1E00A74A00B80903 /* SwiftyCamViewController.swift in Sources */,
168505EA1E288D80005B4537 /* PhotoViewController.swift in Sources */, 168505EA1E288D80005B4537 /* PhotoViewController.swift in Sources */,
056AAB091F97CB1700F6A978 /* Orientation.swift in Sources */,
16298B561E2703DC0056D413 /* SwiftyRecordButton.swift in Sources */, 16298B561E2703DC0056D413 /* SwiftyRecordButton.swift in Sources */,
1675A98D1E00A74A00B80903 /* PreviewView.swift in Sources */, 1675A98D1E00A74A00B80903 /* PreviewView.swift in Sources */,
1675A98E1E00A74A00B80903 /* SwiftyCamButton.swift in Sources */, 1675A98E1E00A74A00B80903 /* SwiftyCamButton.swift in Sources */,
@ -294,7 +300,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "com.Cappsule.SwiftyCam-iOS"; PRODUCT_BUNDLE_IDENTIFIER = "com.Cappsule.SwiftyCam-iOS";
PRODUCT_NAME = SwiftyCam; PRODUCT_NAME = SwiftyCam;
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_VERSION = 3.0; SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = ""; VERSION_INFO_PREFIX = "";
@ -317,7 +323,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "com.Cappsule.SwiftyCam-iOS"; PRODUCT_BUNDLE_IDENTIFIER = "com.Cappsule.SwiftyCam-iOS";
PRODUCT_NAME = SwiftyCam; PRODUCT_NAME = SwiftyCam;
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SWIFT_VERSION = 3.0; SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = ""; VERSION_INFO_PREFIX = "";
@ -333,15 +339,23 @@
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES; CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
@ -371,6 +385,8 @@
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.0;
}; };
name = Debug; name = Debug;
}; };
@ -383,15 +399,23 @@
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_SUSPICIOUS_MOVES = YES; CLANG_WARN_SUSPICIOUS_MOVES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
@ -413,6 +437,8 @@
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.0;
VALIDATE_PRODUCT = YES; VALIDATE_PRODUCT = YES;
}; };
name = Release; name = Release;
@ -421,13 +447,13 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = DGV5BLXSF9; DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = DemoSwiftyCam/Info.plist; INFOPLIST_FILE = DemoSwiftyCam/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.Walzy.DemoSwiftyCam; PRODUCT_BUNDLE_IDENTIFIER = com.Walzy.DemoSwiftyCam1;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0; SWIFT_VERSION = 4.2;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Debug; name = Debug;
@ -436,13 +462,13 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
DEVELOPMENT_TEAM = DGV5BLXSF9; DEVELOPMENT_TEAM = "";
INFOPLIST_FILE = DemoSwiftyCam/Info.plist; INFOPLIST_FILE = DemoSwiftyCam/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.Walzy.DemoSwiftyCam; PRODUCT_BUNDLE_IDENTIFIER = com.Walzy.DemoSwiftyCam1;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0; SWIFT_VERSION = 4.2;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
}; };
name = Release; name = Release;

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "0820" LastUpgradeVersion = "1010"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "0820" LastUpgradeVersion = "0900"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
@ -26,6 +26,7 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<Testables> <Testables>
</Testables> </Testables>
@ -45,6 +46,7 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0" launchStyle = "0"
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO" ignoresPersistentStateOnLaunch = "NO"

View file

@ -22,7 +22,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow? var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch. // Override point for customization after application launch.
return true return true
} }

View file

@ -2,6 +2,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>NSPhotoLibraryUsageDescription</key>
<string>To save videos</string>
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>en</string> <string>en</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>

View file

@ -36,16 +36,16 @@ class PhotoViewController: UIViewController {
super.viewDidLoad() super.viewDidLoad()
self.view.backgroundColor = UIColor.gray self.view.backgroundColor = UIColor.gray
let backgroundImageView = UIImageView(frame: view.frame) let backgroundImageView = UIImageView(frame: view.frame)
backgroundImageView.contentMode = UIViewContentMode.scaleAspectFit backgroundImageView.contentMode = UIView.ContentMode.scaleAspectFit
backgroundImageView.image = backgroundImage backgroundImageView.image = backgroundImage
view.addSubview(backgroundImageView) view.addSubview(backgroundImageView)
let cancelButton = UIButton(frame: CGRect(x: 10.0, y: 10.0, width: 30.0, height: 30.0)) let cancelButton = UIButton(frame: CGRect(x: 10.0, y: 10.0, width: 30.0, height: 30.0))
cancelButton.setImage(#imageLiteral(resourceName: "cancel"), for: UIControlState()) cancelButton.setImage(#imageLiteral(resourceName: "cancel"), for: UIControl.State())
cancelButton.addTarget(self, action: #selector(cancel), for: .touchUpInside) cancelButton.addTarget(self, action: #selector(cancel), for: .touchUpInside)
view.addSubview(cancelButton) view.addSubview(cancelButton)
} }
func cancel() { @objc func cancel() {
dismiss(animated: true, completion: nil) dismiss(animated: true, completion: nil)
} }
} }

View file

@ -49,15 +49,32 @@ class VideoViewController: UIViewController {
playerController!.showsPlaybackControls = false playerController!.showsPlaybackControls = false
playerController!.player = player! playerController!.player = player!
self.addChildViewController(playerController!) self.addChild(playerController!)
self.view.addSubview(playerController!.view) self.view.addSubview(playerController!.view)
playerController!.view.frame = view.frame playerController!.view.frame = view.frame
NotificationCenter.default.addObserver(self, selector: #selector(playerItemDidReachEnd), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: self.player!.currentItem) NotificationCenter.default.addObserver(self, selector: #selector(playerItemDidReachEnd), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: self.player!.currentItem)
let cancelButton = UIButton(frame: CGRect(x: 10.0, y: 10.0, width: 30.0, height: 30.0)) let cancelButton = UIButton(frame: CGRect(x: 10.0, y: 10.0, width: 30.0, height: 30.0))
cancelButton.setImage(#imageLiteral(resourceName: "cancel"), for: UIControlState()) cancelButton.setImage(#imageLiteral(resourceName: "cancel"), for: UIControl.State())
cancelButton.addTarget(self, action: #selector(cancel), for: .touchUpInside) cancelButton.addTarget(self, action: #selector(cancel), for: .touchUpInside)
view.addSubview(cancelButton) view.addSubview(cancelButton)
// Allow background audio to continue to play
do {
if #available(iOS 10.0, *) {
try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.ambient, mode: .default, options: [])
} else {
}
} catch let error as NSError {
print(error)
}
do {
try AVAudioSession.sharedInstance().setActive(true)
} catch let error as NSError {
print(error)
}
} }
override func viewDidAppear(_ animated: Bool) { override func viewDidAppear(_ animated: Bool) {
@ -65,14 +82,19 @@ class VideoViewController: UIViewController {
player?.play() player?.play()
} }
func cancel() { @objc func cancel() {
dismiss(animated: true, completion: nil) dismiss(animated: true, completion: nil)
} }
@objc fileprivate func playerItemDidReachEnd(_ notification: Notification) { @objc fileprivate func playerItemDidReachEnd(_ notification: Notification) {
if self.player != nil { if self.player != nil {
self.player!.seek(to: kCMTimeZero) self.player!.seek(to: CMTime.zero)
self.player!.play() self.player!.play()
} }
} }
} }
// Helper function inserted by Swift 4.2 migrator.
fileprivate func convertFromAVAudioSessionCategory(_ input: AVAudioSession.Category) -> String {
return input.rawValue
}

View file

@ -15,21 +15,25 @@ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
import UIKit import UIKit
import AVFoundation
class ViewController: SwiftyCamViewController, SwiftyCamViewControllerDelegate { class ViewController: SwiftyCamViewController, SwiftyCamViewControllerDelegate {
@IBOutlet weak var captureButton: SwiftyRecordButton! @IBOutlet weak var captureButton : SwiftyRecordButton!
@IBOutlet weak var flipCameraButton: UIButton! @IBOutlet weak var flipCameraButton : UIButton!
@IBOutlet weak var flashButton: UIButton! @IBOutlet weak var flashButton : UIButton!
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
shouldPrompToAppSettings = true
cameraDelegate = self cameraDelegate = self
maximumVideoDuration = 10.0 maximumVideoDuration = 10.0
shouldUseDeviceOrientation = true shouldUseDeviceOrientation = true
allowAutoRotate = true allowAutoRotate = true
audioEnabled = true audioEnabled = true
// disable capture button until session starts
captureButton.buttonEnabled = false
} }
override var prefersStatusBarHidden: Bool { override var prefersStatusBarHidden: Bool {
@ -40,6 +44,17 @@ class ViewController: SwiftyCamViewController, SwiftyCamViewControllerDelegate {
super.viewDidAppear(animated) super.viewDidAppear(animated)
captureButton.delegate = self captureButton.delegate = self
} }
func swiftyCamSessionDidStartRunning(_ swiftyCam: SwiftyCamViewController) {
print("Session did start running")
captureButton.buttonEnabled = true
}
func swiftyCamSessionDidStopRunning(_ swiftyCam: SwiftyCamViewController) {
print("Session did stop running")
captureButton.buttonEnabled = false
}
func swiftyCam(_ swiftyCam: SwiftyCamViewController, didTake photo: UIImage) { func swiftyCam(_ swiftyCam: SwiftyCamViewController, didTake photo: UIImage) {
let newVC = PhotoViewController(image: photo) let newVC = PhotoViewController(image: photo)
@ -49,19 +64,13 @@ class ViewController: SwiftyCamViewController, SwiftyCamViewControllerDelegate {
func swiftyCam(_ swiftyCam: SwiftyCamViewController, didBeginRecordingVideo camera: SwiftyCamViewController.CameraSelection) { func swiftyCam(_ swiftyCam: SwiftyCamViewController, didBeginRecordingVideo camera: SwiftyCamViewController.CameraSelection) {
print("Did Begin Recording") print("Did Begin Recording")
captureButton.growButton() captureButton.growButton()
UIView.animate(withDuration: 0.25, animations: { hideButtons()
self.flashButton.alpha = 0.0
self.flipCameraButton.alpha = 0.0
})
} }
func swiftyCam(_ swiftyCam: SwiftyCamViewController, didFinishRecordingVideo camera: SwiftyCamViewController.CameraSelection) { func swiftyCam(_ swiftyCam: SwiftyCamViewController, didFinishRecordingVideo camera: SwiftyCamViewController.CameraSelection) {
print("Did finish Recording") print("Did finish Recording")
captureButton.shrinkButton() captureButton.shrinkButton()
UIView.animate(withDuration: 0.25, animations: { showButtons()
self.flashButton.alpha = 1.0
self.flipCameraButton.alpha = 1.0
})
} }
func swiftyCam(_ swiftyCam: SwiftyCamViewController, didFinishProcessVideoAt url: URL) { func swiftyCam(_ swiftyCam: SwiftyCamViewController, didFinishProcessVideoAt url: URL) {
@ -70,29 +79,24 @@ class ViewController: SwiftyCamViewController, SwiftyCamViewControllerDelegate {
} }
func swiftyCam(_ swiftyCam: SwiftyCamViewController, didFocusAtPoint point: CGPoint) { func swiftyCam(_ swiftyCam: SwiftyCamViewController, didFocusAtPoint point: CGPoint) {
let focusView = UIImageView(image: #imageLiteral(resourceName: "focus")) print("Did focus at point: \(point)")
focusView.center = point focusAnimationAt(point)
focusView.alpha = 0.0
view.addSubview(focusView)
UIView.animate(withDuration: 0.25, delay: 0.0, options: .curveEaseInOut, animations: {
focusView.alpha = 1.0
focusView.transform = CGAffineTransform(scaleX: 1.25, y: 1.25)
}, completion: { (success) in
UIView.animate(withDuration: 0.15, delay: 0.5, options: .curveEaseInOut, animations: {
focusView.alpha = 0.0
focusView.transform = CGAffineTransform(translationX: 0.6, y: 0.6)
}, completion: { (success) in
focusView.removeFromSuperview()
})
})
} }
func swiftyCamDidFailToConfigure(_ swiftyCam: SwiftyCamViewController) {
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))
present(alertController, animated: true, completion: nil)
}
func swiftyCam(_ swiftyCam: SwiftyCamViewController, didChangeZoomLevel zoom: CGFloat) { func swiftyCam(_ swiftyCam: SwiftyCamViewController, didChangeZoomLevel zoom: CGFloat) {
print("Zoom level did change. Level: \(zoom)")
print(zoom) print(zoom)
} }
func swiftyCam(_ swiftyCam: SwiftyCamViewController, didSwitchCameras camera: SwiftyCamViewController.CameraSelection) { func swiftyCam(_ swiftyCam: SwiftyCamViewController, didSwitchCameras camera: SwiftyCamViewController.CameraSelection) {
print("Camera did change to \(camera.rawValue)")
print(camera) print(camera)
} }
@ -106,11 +110,52 @@ class ViewController: SwiftyCamViewController, SwiftyCamViewControllerDelegate {
@IBAction func toggleFlashTapped(_ sender: Any) { @IBAction func toggleFlashTapped(_ sender: Any) {
flashEnabled = !flashEnabled flashEnabled = !flashEnabled
toggleFlashAnimation()
}
}
// UI Animations
extension ViewController {
fileprivate func hideButtons() {
UIView.animate(withDuration: 0.25) {
self.flashButton.alpha = 0.0
self.flipCameraButton.alpha = 0.0
}
}
fileprivate func showButtons() {
UIView.animate(withDuration: 0.25) {
self.flashButton.alpha = 1.0
self.flipCameraButton.alpha = 1.0
}
}
fileprivate func focusAnimationAt(_ point: CGPoint) {
let focusView = UIImageView(image: #imageLiteral(resourceName: "focus"))
focusView.center = point
focusView.alpha = 0.0
view.addSubview(focusView)
UIView.animate(withDuration: 0.25, delay: 0.0, options: .curveEaseInOut, animations: {
focusView.alpha = 1.0
focusView.transform = CGAffineTransform(scaleX: 1.25, y: 1.25)
}) { (success) in
UIView.animate(withDuration: 0.15, delay: 0.5, options: .curveEaseInOut, animations: {
focusView.alpha = 0.0
focusView.transform = CGAffineTransform(translationX: 0.6, y: 0.6)
}) { (success) in
focusView.removeFromSuperview()
}
}
}
fileprivate func toggleFlashAnimation() {
if flashEnabled == true { if flashEnabled == true {
flashButton.setImage(#imageLiteral(resourceName: "flash"), for: UIControlState()) flashButton.setImage(#imageLiteral(resourceName: "flash"), for: UIControl.State())
} else { } else {
flashButton.setImage(#imageLiteral(resourceName: "flashOutline"), for: UIControlState()) flashButton.setImage(#imageLiteral(resourceName: "flashOutline"), for: UIControl.State())
} }
} }
} }

110
Source/Orientation.swift Normal file
View file

@ -0,0 +1,110 @@
/*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 Foundation
import AVFoundation
import UIKit
import CoreMotion
class Orientation {
var shouldUseDeviceOrientation: Bool = false
fileprivate var deviceOrientation : UIDeviceOrientation?
fileprivate let coreMotionManager = CMMotionManager()
init() {
coreMotionManager.accelerometerUpdateInterval = 0.1
}
func start() {
self.deviceOrientation = UIDevice.current.orientation
coreMotionManager.startAccelerometerUpdates(to: .main) { [weak self] (data, error) in
guard let data = data else {
return
}
self?.handleAccelerometerUpdate(data: data)
}
}
func stop() {
self.coreMotionManager.stopAccelerometerUpdates()
self.deviceOrientation = nil
}
func getImageOrientation(forCamera: SwiftyCamViewController.CameraSelection) -> UIImage.Orientation {
guard shouldUseDeviceOrientation, let deviceOrientation = self.deviceOrientation else { return forCamera == .rear ? .right : .leftMirrored }
switch deviceOrientation {
case .landscapeLeft:
return forCamera == .rear ? .up : .downMirrored
case .landscapeRight:
return forCamera == .rear ? .down : .upMirrored
case .portraitUpsideDown:
return forCamera == .rear ? .left : .rightMirrored
default:
return forCamera == .rear ? .right : .leftMirrored
}
}
func getPreviewLayerOrientation() -> AVCaptureVideoOrientation {
// Depends on layout orientation, not device orientation
switch UIApplication.shared.statusBarOrientation {
case .portrait, .unknown:
return AVCaptureVideoOrientation.portrait
case .landscapeLeft:
return AVCaptureVideoOrientation.landscapeLeft
case .landscapeRight:
return AVCaptureVideoOrientation.landscapeRight
case .portraitUpsideDown:
return AVCaptureVideoOrientation.portraitUpsideDown
}
}
func getVideoOrientation() -> AVCaptureVideoOrientation? {
guard shouldUseDeviceOrientation, let deviceOrientation = self.deviceOrientation else { return nil }
switch deviceOrientation {
case .landscapeLeft:
return .landscapeRight
case .landscapeRight:
return .landscapeLeft
case .portraitUpsideDown:
return .portraitUpsideDown
default:
return .portrait
}
}
private func handleAccelerometerUpdate(data: CMAccelerometerData){
if(abs(data.acceleration.y) < abs(data.acceleration.x)){
if(data.acceleration.x > 0){
deviceOrientation = UIDeviceOrientation.landscapeRight
} else {
deviceOrientation = UIDeviceOrientation.landscapeLeft
}
} else{
if(data.acceleration.y > 0){
deviceOrientation = UIDeviceOrientation.portraitUpsideDown
} else {
deviceOrientation = UIDeviceOrientation.portrait
}
}
}
}

View file

@ -55,11 +55,11 @@ class PreviewView: UIView {
let previewlayer = layer as! AVCaptureVideoPreviewLayer let previewlayer = layer as! AVCaptureVideoPreviewLayer
switch gravity { switch gravity {
case .resize: case .resize:
previewlayer.videoGravity = AVLayerVideoGravityResize previewlayer.videoGravity = AVLayerVideoGravity.resize
case .resizeAspect: case .resizeAspect:
previewlayer.videoGravity = AVLayerVideoGravityResizeAspect previewlayer.videoGravity = AVLayerVideoGravity.resizeAspect
case .resizeAspectFill: case .resizeAspectFill:
previewlayer.videoGravity = AVLayerVideoGravityResizeAspectFill previewlayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
} }
return previewlayer return previewlayer
} }

View file

@ -54,6 +54,10 @@ open class SwiftyCamButton: UIButton {
public weak var delegate: SwiftyCamButtonDelegate? public weak var delegate: SwiftyCamButtonDelegate?
// Sets whether button is enabled
public var buttonEnabled = true
/// Maximum duration variable /// Maximum duration variable
fileprivate var timer : Timer? fileprivate var timer : Timer?
@ -76,11 +80,19 @@ open class SwiftyCamButton: UIButton {
/// UITapGestureRecognizer Function /// UITapGestureRecognizer Function
@objc fileprivate func Tap() { @objc fileprivate func Tap() {
guard buttonEnabled == true else {
return
}
delegate?.buttonWasTapped() delegate?.buttonWasTapped()
} }
/// UILongPressGestureRecognizer Function /// UILongPressGestureRecognizer Function
@objc fileprivate func LongPress(_ sender:UILongPressGestureRecognizer!) { @objc fileprivate func LongPress(_ sender:UILongPressGestureRecognizer!) {
guard buttonEnabled == true else {
return
}
switch sender.state { switch sender.state {
case .began: case .began:
delegate?.buttonDidBeginLongPress() delegate?.buttonDidBeginLongPress()

View file

@ -27,13 +27,13 @@ open class SwiftyCamViewController: UIViewController {
/// Enumeration for Camera Selection /// Enumeration for Camera Selection
public enum CameraSelection { public enum CameraSelection: String {
/// Camera on the back of the device /// Camera on the back of the device
case rear case rear = "rear"
/// Camera on the front of the device /// Camera on the front of the device
case front case front = "front"
} }
/// Enumeration for video quality of the capture session. Corresponds to a AVCaptureSessionPreset /// Enumeration for video quality of the capture session. Corresponds to a AVCaptureSessionPreset
@ -112,7 +112,7 @@ open class SwiftyCamViewController: UIViewController {
/// Sets the maximum zoom scale allowed during gestures gesture /// Sets the maximum zoom scale allowed during gestures gesture
public var maxZoomScale = CGFloat.greatestFiniteMagnitude public var maxZoomScale = CGFloat.greatestFiniteMagnitude
/// Sets whether Tap to Focus and Tap to Adjust Exposure is enabled for the capture session /// Sets whether Tap to Focus and Tap to Adjust Exposure is enabled for the capture session
public var tapToFocus = true public var tapToFocus = true
@ -130,13 +130,13 @@ open class SwiftyCamViewController: UIViewController {
/// Sets whether a double tap to switch cameras is supported /// Sets whether a double tap to switch cameras is supported
public var doubleTapCameraSwitch = true public var doubleTapCameraSwitch = true
/// Sets whether swipe vertically to zoom is supported /// Sets whether swipe vertically to zoom is supported
public var swipeToZoom = true public var swipeToZoom = true
/// Sets whether swipe vertically gestures should be inverted /// Sets whether swipe vertically gestures should be inverted
public var swipeToZoomInverted = false public var swipeToZoomInverted = false
/// Set default launch camera /// Set default launch camera
@ -145,22 +145,30 @@ open class SwiftyCamViewController: UIViewController {
/// Sets wether the taken photo or video should be oriented according to the device orientation /// Sets wether the taken photo or video should be oriented according to the device orientation
public var shouldUseDeviceOrientation = false public var shouldUseDeviceOrientation = false {
didSet {
orientation.shouldUseDeviceOrientation = shouldUseDeviceOrientation
}
}
/// Sets whether or not View Controller supports auto rotation /// Sets whether or not View Controller supports auto rotation
public var allowAutoRotate = false public var allowAutoRotate = false
/// Specifies the [videoGravity](https://developer.apple.com/reference/avfoundation/avcapturevideopreviewlayer/1386708-videogravity) for the preview layer. /// Specifies the [videoGravity](https://developer.apple.com/reference/avfoundation/avcapturevideopreviewlayer/1386708-videogravity) for the preview layer.
public var videoGravity : SwiftyCamVideoGravity = .resizeAspect public var videoGravity : SwiftyCamVideoGravity = .resizeAspect
/// Sets whether or not video recordings will record audio /// Sets whether or not video recordings will record audio
/// Setting to true will prompt user for access to microphone on View Controller launch. /// Setting to true will prompt user for access to microphone on View Controller launch.
public var audioEnabled = true public var audioEnabled = true
/// Sets whether or not app should display prompt to app settings if audio/video permission is denied
/// If set to false, delegate function will be called to handle exception
public var shouldPrompToAppSettings = true
/// Public access to Pinch Gesture /// Public access to Pinch Gesture
fileprivate(set) public var pinchGesture : UIPinchGestureRecognizer! fileprivate(set) public var pinchGesture : UIPinchGestureRecognizer!
/// Public access to Pan Gesture /// Public access to Pan Gesture
fileprivate(set) public var panGesture : UIPanGestureRecognizer! fileprivate(set) public var panGesture : UIPanGestureRecognizer!
@ -234,14 +242,18 @@ open class SwiftyCamViewController: UIViewController {
/// UIView for front facing flash /// UIView for front facing flash
fileprivate var flashView : UIView? fileprivate var flashView : UIView?
/// Pan Translation /// Pan Translation
fileprivate var previousPanTranslation : CGFloat = 0.0 fileprivate var previousPanTranslation : CGFloat = 0.0
/// Last changed orientation /// Last changed orientation
fileprivate var deviceOrientation : UIDeviceOrientation? fileprivate var orientation : Orientation = Orientation()
/// Boolean to store when View Controller is notified session is running
fileprivate var sessionRunning = false
/// Disable view autorotation for forced portrait recorindg /// Disable view autorotation for forced portrait recorindg
@ -258,17 +270,17 @@ open class SwiftyCamViewController: UIViewController {
previewLayer = PreviewView(frame: view.frame, videoGravity: videoGravity) previewLayer = PreviewView(frame: view.frame, videoGravity: videoGravity)
previewLayer.center = view.center previewLayer.center = view.center
view.addSubview(previewLayer) view.addSubview(previewLayer)
view.sendSubview(toBack: previewLayer) view.sendSubviewToBack(previewLayer)
// Add Gesture Recognizers // Add Gesture Recognizers
addGestureRecognizers() addGestureRecognizers()
previewLayer.session = session previewLayer.session = session
// Test authorization status for Camera and Micophone // Test authorization status for Camera and Micophone
switch AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo){ switch AVCaptureDevice.authorizationStatus(for: AVMediaType.video) {
case .authorized: case .authorized:
// already authorized // already authorized
@ -277,7 +289,7 @@ open class SwiftyCamViewController: UIViewController {
// not yet determined // not yet determined
sessionQueue.suspend() sessionQueue.suspend()
AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo, completionHandler: { [unowned self] granted in AVCaptureDevice.requestAccess(for: AVMediaType.video, completionHandler: { [unowned self] granted in
if !granted { if !granted {
self.setupResult = .notAuthorized self.setupResult = .notAuthorized
} }
@ -294,65 +306,74 @@ open class SwiftyCamViewController: UIViewController {
} }
// MARK: ViewDidLayoutSubviews // MARK: ViewDidLayoutSubviews
/// ViewDidLayoutSubviews() Implementation /// ViewDidLayoutSubviews() Implementation
private func updatePreviewLayer(layer: AVCaptureConnection, orientation: AVCaptureVideoOrientation) { private func updatePreviewLayer(layer: AVCaptureConnection, orientation: AVCaptureVideoOrientation) {
layer.videoOrientation = orientation layer.videoOrientation = orientation
previewLayer.frame = self.view.bounds previewLayer.frame = self.view.bounds
} }
override open func viewDidLayoutSubviews() { override open func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews() super.viewDidLayoutSubviews()
if let connection = self.previewLayer?.videoPreviewLayer.connection { if let connection = self.previewLayer?.videoPreviewLayer.connection {
let currentDevice: UIDevice = UIDevice.current let currentDevice: UIDevice = UIDevice.current
let orientation: UIDeviceOrientation = currentDevice.orientation let orientation: UIDeviceOrientation = currentDevice.orientation
let previewLayerConnection : AVCaptureConnection = connection let previewLayerConnection : AVCaptureConnection = connection
if previewLayerConnection.isVideoOrientationSupported { if previewLayerConnection.isVideoOrientationSupported {
switch (orientation) { switch (orientation) {
case .portrait: updatePreviewLayer(layer: previewLayerConnection, orientation: .portrait) case .portrait: updatePreviewLayer(layer: previewLayerConnection, orientation: .portrait)
break break
case .landscapeRight: updatePreviewLayer(layer: previewLayerConnection, orientation: .landscapeLeft) case .landscapeRight: updatePreviewLayer(layer: previewLayerConnection, orientation: .landscapeLeft)
break break
case .landscapeLeft: updatePreviewLayer(layer: previewLayerConnection, orientation: .landscapeRight) case .landscapeLeft: updatePreviewLayer(layer: previewLayerConnection, orientation: .landscapeRight)
break break
case .portraitUpsideDown: updatePreviewLayer(layer: previewLayerConnection, orientation: .portraitUpsideDown) case .portraitUpsideDown: updatePreviewLayer(layer: previewLayerConnection, orientation: .portraitUpsideDown)
break break
default: updatePreviewLayer(layer: previewLayerConnection, orientation: .portrait) default: updatePreviewLayer(layer: previewLayerConnection, orientation: .portrait)
break break
} }
} }
} }
} }
// MARK: ViewWillAppear
/// ViewWillAppear(_ animated:) Implementation
open override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(captureSessionDidStartRunning), name: .AVCaptureSessionDidStartRunning, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(captureSessionDidStopRunning), name: .AVCaptureSessionDidStopRunning, object: nil)
}
// MARK: ViewDidAppear // MARK: ViewDidAppear
/// ViewDidAppear(_ animated:) Implementation /// ViewDidAppear(_ animated:) Implementation
override open func viewDidAppear(_ animated: Bool) { override open func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated) super.viewDidAppear(animated)
// Subscribe to device rotation notifications // Subscribe to device rotation notifications
if shouldUseDeviceOrientation { if shouldUseDeviceOrientation {
subscribeToDeviceOrientationChangeNotifications() orientation.start()
} }
// Set background audio preference // Set background audio preference
@ -365,23 +386,23 @@ open class SwiftyCamViewController: UIViewController {
// Begin Session // Begin Session
self.session.startRunning() self.session.startRunning()
self.isSessionRunning = self.session.isRunning self.isSessionRunning = self.session.isRunning
// Preview layer video orientation can be set only after the connection is created // Preview layer video orientation can be set only after the connection is created
DispatchQueue.main.async { DispatchQueue.main.async {
self.previewLayer.videoPreviewLayer.connection?.videoOrientation = self.getPreviewLayerOrientation() self.previewLayer.videoPreviewLayer.connection?.videoOrientation = self.orientation.getPreviewLayerOrientation()
} }
case .notAuthorized: case .notAuthorized:
// Prompt to App Settings if self.shouldPrompToAppSettings == true {
self.promptToAppSettings() self.promptToAppSettings()
} else {
self.cameraDelegate?.swiftyCamNotAuthorized(self)
}
case .configurationFailed: case .configurationFailed:
// Unknown Error // Unknown Error
DispatchQueue.main.async(execute: { [unowned self] in DispatchQueue.main.async {
let message = NSLocalizedString("Unable to capture media", comment: "Alert message when something goes wrong during capture session configuration") self.cameraDelegate?.swiftyCamDidFailToConfigure(self)
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)
})
} }
} }
} }
@ -394,6 +415,9 @@ open class SwiftyCamViewController: UIViewController {
override open func viewDidDisappear(_ animated: Bool) { override open func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated) super.viewDidDisappear(animated)
NotificationCenter.default.removeObserver(self)
sessionRunning = false
// If session is running, stop the session // If session is running, stop the session
if self.isSessionRunning == true { if self.isSessionRunning == true {
self.session.stopRunning() self.session.stopRunning()
@ -405,7 +429,7 @@ open class SwiftyCamViewController: UIViewController {
// Unsubscribe from device rotation notifications // Unsubscribe from device rotation notifications
if shouldUseDeviceOrientation { if shouldUseDeviceOrientation {
unsubscribeFromDeviceOrientationChangeNotifications() orientation.stop()
} }
} }
@ -425,6 +449,7 @@ open class SwiftyCamViewController: UIViewController {
return return
} }
if device.hasFlash == true && flashEnabled == true /* TODO: Add Support for Retina Flash and add front flash */ { if device.hasFlash == true && flashEnabled == true /* TODO: Add Support for Retina Flash and add front flash */ {
changeFlashSettings(device: device, mode: .on) changeFlashSettings(device: device, mode: .on)
capturePhotoAsyncronously(completionHandler: { (_) in }) capturePhotoAsyncronously(completionHandler: { (_) in })
@ -464,6 +489,11 @@ open class SwiftyCamViewController: UIViewController {
*/ */
public func startVideoRecording() { public func startVideoRecording() {
guard sessionRunning == true else {
print("[SwiftyCam]: Cannot start video recoding. Capture session is not running")
return
}
guard let movieFileOutput = self.movieFileOutput else { guard let movieFileOutput = self.movieFileOutput else {
return return
} }
@ -479,6 +509,9 @@ open class SwiftyCamViewController: UIViewController {
previewLayer.addSubview(flashView!) previewLayer.addSubview(flashView!)
} }
//Must be fetched before on main thread
let previewOrientation = previewLayer.videoPreviewLayer.connection!.videoOrientation
sessionQueue.async { [unowned self] in sessionQueue.async { [unowned self] in
if !movieFileOutput.isRecording { if !movieFileOutput.isRecording {
if UIDevice.current.isMultitaskingSupported { if UIDevice.current.isMultitaskingSupported {
@ -486,7 +519,7 @@ open class SwiftyCamViewController: UIViewController {
} }
// Update the orientation on the movie file output video connection before starting recording. // Update the orientation on the movie file output video connection before starting recording.
let movieFileOutputConnection = self.movieFileOutput?.connection(withMediaType: AVMediaTypeVideo) let movieFileOutputConnection = self.movieFileOutput?.connection(with: AVMediaType.video)
//flip video output if front facing camera is selected //flip video output if front facing camera is selected
@ -494,12 +527,12 @@ open class SwiftyCamViewController: UIViewController {
movieFileOutputConnection?.isVideoMirrored = true movieFileOutputConnection?.isVideoMirrored = true
} }
movieFileOutputConnection?.videoOrientation = self.getVideoOrientation() movieFileOutputConnection?.videoOrientation = self.orientation.getVideoOrientation() ?? previewOrientation
// Start recording to a temporary file. // Start recording to a temporary file.
let outputFileName = UUID().uuidString let outputFileName = UUID().uuidString
let outputFilePath = (NSTemporaryDirectory() as NSString).appendingPathComponent((outputFileName as NSString).appendingPathExtension("mov")!) let outputFilePath = (NSTemporaryDirectory() as NSString).appendingPathComponent((outputFileName as NSString).appendingPathExtension("mov")!)
movieFileOutput.startRecording(toOutputFileURL: URL(fileURLWithPath: outputFilePath), recordingDelegate: self) movieFileOutput.startRecording(to: URL(fileURLWithPath: outputFilePath), recordingDelegate: self)
self.isVideoRecording = true self.isVideoRecording = true
DispatchQueue.main.async { DispatchQueue.main.async {
self.cameraDelegate?.swiftyCam(self, didBeginRecordingVideo: self.currentCamera) self.cameraDelegate?.swiftyCam(self, didBeginRecordingVideo: self.currentCamera)
@ -555,11 +588,11 @@ open class SwiftyCamViewController: UIViewController {
print("[SwiftyCam]: Switching between cameras while recording video is not supported") print("[SwiftyCam]: Switching between cameras while recording video is not supported")
return return
} }
guard session.isRunning == true else { guard session.isRunning == true else {
return return
} }
switch currentCamera { switch currentCamera {
case .front: case .front:
currentCamera = .rear currentCamera = .rear
@ -574,7 +607,7 @@ open class SwiftyCamViewController: UIViewController {
// remove and re-add inputs and outputs // remove and re-add inputs and outputs
for input in self.session.inputs { for input in self.session.inputs {
self.session.removeInput(input as! AVCaptureInput) self.session.removeInput(input )
} }
self.addInputs() self.addInputs()
@ -631,12 +664,12 @@ open class SwiftyCamViewController: UIViewController {
fileprivate func configureVideoPreset() { fileprivate func configureVideoPreset() {
if currentCamera == .front { if currentCamera == .front {
session.sessionPreset = videoInputPresetFromVideoQuality(quality: .high) session.sessionPreset = AVCaptureSession.Preset(rawValue: videoInputPresetFromVideoQuality(quality: .high))
} else { } else {
if session.canSetSessionPreset(videoInputPresetFromVideoQuality(quality: videoQuality)) { if session.canSetSessionPreset(AVCaptureSession.Preset(rawValue: videoInputPresetFromVideoQuality(quality: videoQuality))) {
session.sessionPreset = videoInputPresetFromVideoQuality(quality: videoQuality) session.sessionPreset = AVCaptureSession.Preset(rawValue: videoInputPresetFromVideoQuality(quality: videoQuality))
} else { } else {
session.sessionPreset = videoInputPresetFromVideoQuality(quality: .high) session.sessionPreset = AVCaptureSession.Preset(rawValue: videoInputPresetFromVideoQuality(quality: .high))
} }
} }
} }
@ -646,9 +679,9 @@ open class SwiftyCamViewController: UIViewController {
fileprivate func addVideoInput() { fileprivate func addVideoInput() {
switch currentCamera { switch currentCamera {
case .front: case .front:
videoDevice = SwiftyCamViewController.deviceWithMediaType(AVMediaTypeVideo, preferringPosition: .front) videoDevice = SwiftyCamViewController.deviceWithMediaType(AVMediaType.video.rawValue, preferringPosition: .front)
case .rear: case .rear:
videoDevice = SwiftyCamViewController.deviceWithMediaType(AVMediaTypeVideo, preferringPosition: .back) videoDevice = SwiftyCamViewController.deviceWithMediaType(AVMediaType.video.rawValue, preferringPosition: .back)
} }
if let device = videoDevice { if let device = videoDevice {
@ -680,18 +713,20 @@ open class SwiftyCamViewController: UIViewController {
} }
do { do {
let videoDeviceInput = try AVCaptureDeviceInput(device: videoDevice) if let videoDevice = videoDevice {
let videoDeviceInput = try AVCaptureDeviceInput(device: videoDevice)
if session.canAddInput(videoDeviceInput) { if session.canAddInput(videoDeviceInput) {
session.addInput(videoDeviceInput) session.addInput(videoDeviceInput)
self.videoDeviceInput = videoDeviceInput self.videoDeviceInput = videoDeviceInput
} else { } else {
print("[SwiftyCam]: Could not add video device input to the session") print("[SwiftyCam]: Could not add video device input to the session")
print(session.canSetSessionPreset(videoInputPresetFromVideoQuality(quality: videoQuality))) print(session.canSetSessionPreset(AVCaptureSession.Preset(rawValue: videoInputPresetFromVideoQuality(quality: videoQuality))))
setupResult = .configurationFailed setupResult = .configurationFailed
session.commitConfiguration() session.commitConfiguration()
return return
} }
}
} catch { } catch {
print("[SwiftyCam]: Could not create video device input: \(error)") print("[SwiftyCam]: Could not create video device input: \(error)")
setupResult = .configurationFailed setupResult = .configurationFailed
@ -706,17 +741,19 @@ open class SwiftyCamViewController: UIViewController {
return return
} }
do { do {
let audioDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeAudio) if let audioDevice = AVCaptureDevice.default(for: AVMediaType.audio){
let audioDeviceInput = try AVCaptureDeviceInput(device: audioDevice) let audioDeviceInput = try AVCaptureDeviceInput(device: audioDevice)
if session.canAddInput(audioDeviceInput) {
if session.canAddInput(audioDeviceInput) { session.addInput(audioDeviceInput)
session.addInput(audioDeviceInput) } else {
} print("[SwiftyCam]: Could not add audio device input to the session")
else { }
print("[SwiftyCam]: Could not add audio device input to the session")
} } else {
} print("[SwiftyCam]: Could not find an audio device")
catch { }
} catch {
print("[SwiftyCam]: Could not create audio device input: \(error)") print("[SwiftyCam]: Could not create audio device input: \(error)")
} }
} }
@ -728,7 +765,7 @@ open class SwiftyCamViewController: UIViewController {
if self.session.canAddOutput(movieFileOutput) { if self.session.canAddOutput(movieFileOutput) {
self.session.addOutput(movieFileOutput) self.session.addOutput(movieFileOutput)
if let connection = movieFileOutput.connection(withMediaType: AVMediaTypeVideo) { if let connection = movieFileOutput.connection(with: AVMediaType.video) {
if connection.isVideoStabilizationSupported { if connection.isVideoStabilizationSupported {
connection.preferredVideoStabilizationMode = .auto connection.preferredVideoStabilizationMode = .auto
} }
@ -749,67 +786,6 @@ open class SwiftyCamViewController: UIViewController {
} }
} }
/// Orientation management
fileprivate func subscribeToDeviceOrientationChangeNotifications() {
self.deviceOrientation = UIDevice.current.orientation
NotificationCenter.default.addObserver(self, selector: #selector(deviceDidRotate), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
}
fileprivate func unsubscribeFromDeviceOrientationChangeNotifications() {
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
self.deviceOrientation = nil
}
@objc fileprivate func deviceDidRotate() {
if !UIDevice.current.orientation.isFlat {
self.deviceOrientation = UIDevice.current.orientation
}
}
fileprivate func getPreviewLayerOrientation() -> AVCaptureVideoOrientation {
// Depends on layout orientation, not device orientation
switch UIApplication.shared.statusBarOrientation {
case .portrait, .unknown:
return AVCaptureVideoOrientation.portrait
case .landscapeLeft:
return AVCaptureVideoOrientation.landscapeLeft
case .landscapeRight:
return AVCaptureVideoOrientation.landscapeRight
case .portraitUpsideDown:
return AVCaptureVideoOrientation.portraitUpsideDown
}
}
fileprivate func getVideoOrientation() -> AVCaptureVideoOrientation {
guard shouldUseDeviceOrientation, let deviceOrientation = self.deviceOrientation else { return previewLayer!.videoPreviewLayer.connection.videoOrientation }
switch deviceOrientation {
case .landscapeLeft:
return .landscapeRight
case .landscapeRight:
return .landscapeLeft
case .portraitUpsideDown:
return .portraitUpsideDown
default:
return .portrait
}
}
fileprivate func getImageOrientation(forCamera: CameraSelection) -> UIImageOrientation {
guard shouldUseDeviceOrientation, let deviceOrientation = self.deviceOrientation else { return forCamera == .rear ? .right : .leftMirrored }
switch deviceOrientation {
case .landscapeLeft:
return forCamera == .rear ? .up : .downMirrored
case .landscapeRight:
return forCamera == .rear ? .down : .upMirrored
case .portraitUpsideDown:
return forCamera == .rear ? .left : .rightMirrored
default:
return forCamera == .rear ? .right : .leftMirrored
}
}
/** /**
Returns a UIImage from Image Data. Returns a UIImage from Image Data.
@ -826,17 +802,23 @@ open class SwiftyCamViewController: UIViewController {
// Set proper orientation for photo // Set proper orientation for photo
// If camera is currently set to front camera, flip image // If camera is currently set to front camera, flip image
let image = UIImage(cgImage: cgImageRef!, scale: 1.0, orientation: self.getImageOrientation(forCamera: self.currentCamera)) let image = UIImage(cgImage: cgImageRef!, scale: 1.0, orientation: self.orientation.getImageOrientation(forCamera: self.currentCamera))
return image return image
} }
fileprivate func capturePhotoAsyncronously(completionHandler: @escaping(Bool) -> ()) { fileprivate func capturePhotoAsyncronously(completionHandler: @escaping(Bool) -> ()) {
if let videoConnection = photoFileOutput?.connection(withMediaType: AVMediaTypeVideo) {
guard sessionRunning == true else {
print("[SwiftyCam]: Cannot take photo. Capture session is not running")
return
}
if let videoConnection = photoFileOutput?.connection(with: AVMediaType.video) {
photoFileOutput?.captureStillImageAsynchronously(from: videoConnection, completionHandler: {(sampleBuffer, error) in photoFileOutput?.captureStillImageAsynchronously(from: videoConnection, completionHandler: {(sampleBuffer, error) in
if (sampleBuffer != nil) { if (sampleBuffer != nil) {
let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer) let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer!)
let image = self.processPhoto(imageData!) let image = self.processPhoto(imageData!)
// Call delegate and return new image // Call delegate and return new image
@ -864,9 +846,9 @@ open class SwiftyCamViewController: UIViewController {
alertController.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "Alert OK button"), style: .cancel, handler: nil)) 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 alertController.addAction(UIAlertAction(title: NSLocalizedString("Settings", comment: "Alert button to open Settings"), style: .default, handler: { action in
if #available(iOS 10.0, *) { if #available(iOS 10.0, *) {
UIApplication.shared.openURL(URL(string: UIApplicationOpenSettingsURLString)!) UIApplication.shared.openURL(URL(string: UIApplication.openSettingsURLString)!)
} else { } else {
if let appSettings = URL(string: UIApplicationOpenSettingsURLString) { if let appSettings = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.openURL(appSettings) UIApplication.shared.openURL(appSettings)
} }
} }
@ -885,38 +867,54 @@ open class SwiftyCamViewController: UIViewController {
fileprivate func videoInputPresetFromVideoQuality(quality: VideoQuality) -> String { fileprivate func videoInputPresetFromVideoQuality(quality: VideoQuality) -> String {
switch quality { switch quality {
case .high: return AVCaptureSessionPresetHigh case .high: return AVCaptureSession.Preset.high.rawValue
case .medium: return AVCaptureSessionPresetMedium case .medium: return AVCaptureSession.Preset.medium.rawValue
case .low: return AVCaptureSessionPresetLow case .low: return AVCaptureSession.Preset.low.rawValue
case .resolution352x288: return AVCaptureSessionPreset352x288 case .resolution352x288: return AVCaptureSession.Preset.cif352x288.rawValue
case .resolution640x480: return AVCaptureSessionPreset640x480 case .resolution640x480: return AVCaptureSession.Preset.vga640x480.rawValue
case .resolution1280x720: return AVCaptureSessionPreset1280x720 case .resolution1280x720: return AVCaptureSession.Preset.hd1280x720.rawValue
case .resolution1920x1080: return AVCaptureSessionPreset1920x1080 case .resolution1920x1080: return AVCaptureSession.Preset.hd1920x1080.rawValue
case .iframe960x540: return AVCaptureSessionPresetiFrame960x540 case .iframe960x540: return AVCaptureSession.Preset.iFrame960x540.rawValue
case .iframe1280x720: return AVCaptureSessionPresetiFrame1280x720 case .iframe1280x720: return AVCaptureSession.Preset.iFrame1280x720.rawValue
case .resolution3840x2160: case .resolution3840x2160:
if #available(iOS 9.0, *) { if #available(iOS 9.0, *) {
return AVCaptureSessionPreset3840x2160 return AVCaptureSession.Preset.hd4K3840x2160.rawValue
} }
else { else {
print("[SwiftyCam]: Resolution 3840x2160 not supported") print("[SwiftyCam]: Resolution 3840x2160 not supported")
return AVCaptureSessionPresetHigh return AVCaptureSession.Preset.high.rawValue
} }
} }
} }
/// Get Devices /// Get Devices
fileprivate class func deviceWithMediaType(_ mediaType: String, preferringPosition position: AVCaptureDevicePosition) -> AVCaptureDevice? { fileprivate class func deviceWithMediaType(_ mediaType: String, preferringPosition position: AVCaptureDevice.Position) -> AVCaptureDevice? {
if let devices = AVCaptureDevice.devices(withMediaType: mediaType) as? [AVCaptureDevice] { if #available(iOS 10.0, *) {
return devices.filter({ $0.position == position }).first let avDevice = AVCaptureDevice.default(AVCaptureDevice.DeviceType.builtInWideAngleCamera, for: AVMediaType(rawValue: mediaType), position: position)
return avDevice
} else {
// Fallback on earlier versions
let avDevice = AVCaptureDevice.devices(for: AVMediaType(rawValue: mediaType))
var avDeviceNum = 0
for device in avDevice {
print("deviceWithMediaType Position: \(device.position.rawValue)")
if device.position == position {
break
} else {
avDeviceNum += 1
}
}
return avDevice[avDeviceNum]
} }
return nil
//return AVCaptureDevice.devices(for: AVMediaType(rawValue: mediaType), position: position).first
} }
/// Enable or disable flash for photo /// Enable or disable flash for photo
fileprivate func changeFlashSettings(device: AVCaptureDevice, mode: AVCaptureFlashMode) { fileprivate func changeFlashSettings(device: AVCaptureDevice, mode: AVCaptureDevice.FlashMode) {
do { do {
try device.lockForConfiguration() try device.lockForConfiguration()
device.flashMode = mode device.flashMode = mode
@ -950,17 +948,17 @@ open class SwiftyCamViewController: UIViewController {
return return
} }
let device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo) let device = AVCaptureDevice.default(for: AVMediaType.video)
// Check if device has a flash // Check if device has a flash
if (device?.hasTorch)! { if (device?.hasTorch)! {
do { do {
try device?.lockForConfiguration() try device?.lockForConfiguration()
if (device?.torchMode == AVCaptureTorchMode.on) { if (device?.torchMode == AVCaptureDevice.TorchMode.on) {
device?.torchMode = AVCaptureTorchMode.off device?.torchMode = AVCaptureDevice.TorchMode.off
self.isCameraTorchOn = false self.isCameraTorchOn = false
} else { } else {
do { do {
try device?.setTorchModeOnWithLevel(1.0) try device?.setTorchModeOn(level: 1.0)
self.isCameraTorchOn = true self.isCameraTorchOn = true
} catch { } catch {
print("[SwiftyCam]: \(error)") print("[SwiftyCam]: \(error)")
@ -979,15 +977,21 @@ open class SwiftyCamViewController: UIViewController {
guard allowBackgroundAudio == true else { guard allowBackgroundAudio == true else {
return return
} }
guard audioEnabled == true else { guard audioEnabled == true else {
return return
} }
do{ do{
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord, if #available(iOS 10.0, *) {
with: [.duckOthers, .defaultToSpeaker]) try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .default, options: [.mixWithOthers, .allowBluetooth, .allowAirPlay, .allowBluetoothA2DP])
} else {
let options: [AVAudioSession.CategoryOptions] = [.mixWithOthers, .allowBluetooth]
let category = AVAudioSession.Category.playAndRecord
let selector = NSSelectorFromString("setCategory:withOptions:error:")
AVAudioSession.sharedInstance().perform(selector, with: category, with: options)
}
try AVAudioSession.sharedInstance().setActive(true)
session.automaticallyConfiguresApplicationAudioSession = false session.automaticallyConfiguresApplicationAudioSession = false
} }
catch { catch {
@ -995,6 +999,24 @@ open class SwiftyCamViewController: UIViewController {
} }
} }
/// Called when Notification Center registers session starts running
@objc private func captureSessionDidStartRunning() {
sessionRunning = true
DispatchQueue.main.async {
self.cameraDelegate?.swiftyCamSessionDidStartRunning(self)
}
}
/// Called when Notification Center registers session stops running
@objc private func captureSessionDidStopRunning() {
sessionRunning = false
DispatchQueue.main.async {
self.cameraDelegate?.swiftyCamSessionDidStopRunning(self)
}
}
} }
extension SwiftyCamViewController : SwiftyCamButtonDelegate { extension SwiftyCamViewController : SwiftyCamButtonDelegate {
@ -1037,26 +1059,27 @@ extension SwiftyCamViewController : AVCaptureFileOutputRecordingDelegate {
/// Process newly captured video and write it to temporary directory /// Process newly captured video and write it to temporary directory
public func capture(_ captureOutput: AVCaptureFileOutput!, didFinishRecordingToOutputFileAt outputFileURL: URL!, fromConnections connections: [Any]!, error: Error!) { public func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
if let currentBackgroundRecordingID = backgroundRecordingID { if let currentBackgroundRecordingID = backgroundRecordingID {
backgroundRecordingID = UIBackgroundTaskInvalid backgroundRecordingID = UIBackgroundTaskIdentifier.invalid
if currentBackgroundRecordingID != UIBackgroundTaskInvalid { if currentBackgroundRecordingID != UIBackgroundTaskIdentifier.invalid {
UIApplication.shared.endBackgroundTask(currentBackgroundRecordingID) UIApplication.shared.endBackgroundTask(currentBackgroundRecordingID)
}
}
if error != nil {
print("[SwiftyCam]: Movie file finishing error: \(error)")
DispatchQueue.main.async {
self.cameraDelegate?.swiftyCam(self, didFailToRecordVideo: error)
} }
} else { }
//Call delegate function with the URL of the outputfile
DispatchQueue.main.async { if let currentError = error {
self.cameraDelegate?.swiftyCam(self, didFinishProcessVideoAt: outputFileURL) print("[SwiftyCam]: Movie file finishing error: \(currentError)")
} DispatchQueue.main.async {
} self.cameraDelegate?.swiftyCam(self, didFailToRecordVideo: currentError)
} }
} else {
//Call delegate function with the URL of the outputfile
DispatchQueue.main.async {
self.cameraDelegate?.swiftyCam(self, didFinishProcessVideoAt: outputFileURL)
}
}
}
} }
// Mark: UIGestureRecognizer Declarations // Mark: UIGestureRecognizer Declarations
@ -1067,11 +1090,11 @@ extension SwiftyCamViewController {
@objc fileprivate func zoomGesture(pinch: UIPinchGestureRecognizer) { @objc fileprivate func zoomGesture(pinch: UIPinchGestureRecognizer) {
guard pinchToZoom == true && self.currentCamera == .rear else { guard pinchToZoom == true && self.currentCamera == .rear else {
//ignore pinch //ignore pinch
return return
} }
do { do {
let captureDevice = AVCaptureDevice.devices().first as? AVCaptureDevice let captureDevice = AVCaptureDevice.devices().first
try captureDevice?.lockForConfiguration() try captureDevice?.lockForConfiguration()
zoomScale = min(maxZoomScale, max(1.0, min(beginZoomScale * pinch.scale, captureDevice!.activeFormat.videoMaxZoomFactor))) zoomScale = min(maxZoomScale, max(1.0, min(beginZoomScale * pinch.scale, captureDevice!.activeFormat.videoMaxZoomFactor)))
@ -1113,7 +1136,7 @@ extension SwiftyCamViewController {
device.focusMode = .autoFocus device.focusMode = .autoFocus
} }
device.exposurePointOfInterest = focusPoint device.exposurePointOfInterest = focusPoint
device.exposureMode = AVCaptureExposureMode.continuousAutoExposure device.exposureMode = AVCaptureDevice.ExposureMode.continuousAutoExposure
device.unlockForConfiguration() device.unlockForConfiguration()
//Call delegate function and pass in the location of the touch //Call delegate function and pass in the location of the touch
@ -1135,42 +1158,42 @@ extension SwiftyCamViewController {
} }
switchCamera() switchCamera()
} }
@objc private func panGesture(pan: UIPanGestureRecognizer) { @objc private func panGesture(pan: UIPanGestureRecognizer) {
guard swipeToZoom == true && self.currentCamera == .rear else { guard swipeToZoom == true && self.currentCamera == .rear else {
//ignore pan //ignore pan
return return
} }
let currentTranslation = pan.translation(in: view).y let currentTranslation = pan.translation(in: view).y
let translationDifference = currentTranslation - previousPanTranslation let translationDifference = currentTranslation - previousPanTranslation
do { do {
let captureDevice = AVCaptureDevice.devices().first as? AVCaptureDevice let captureDevice = AVCaptureDevice.devices().first
try captureDevice?.lockForConfiguration() try captureDevice?.lockForConfiguration()
let currentZoom = captureDevice?.videoZoomFactor ?? 0.0 let currentZoom = captureDevice?.videoZoomFactor ?? 0.0
if swipeToZoomInverted == true { if swipeToZoomInverted == true {
zoomScale = min(maxZoomScale, max(1.0, min(currentZoom - (translationDifference / 75), captureDevice!.activeFormat.videoMaxZoomFactor))) zoomScale = min(maxZoomScale, max(1.0, min(currentZoom - (translationDifference / 75), captureDevice!.activeFormat.videoMaxZoomFactor)))
} else { } else {
zoomScale = min(maxZoomScale, max(1.0, min(currentZoom + (translationDifference / 75), captureDevice!.activeFormat.videoMaxZoomFactor))) zoomScale = min(maxZoomScale, max(1.0, min(currentZoom + (translationDifference / 75), captureDevice!.activeFormat.videoMaxZoomFactor)))
} }
captureDevice?.videoZoomFactor = zoomScale captureDevice?.videoZoomFactor = zoomScale
// Call Delegate function with current zoom scale // Call Delegate function with current zoom scale
DispatchQueue.main.async { DispatchQueue.main.async {
self.cameraDelegate?.swiftyCam(self, didChangeZoomLevel: self.zoomScale) self.cameraDelegate?.swiftyCam(self, didChangeZoomLevel: self.zoomScale)
} }
captureDevice?.unlockForConfiguration() captureDevice?.unlockForConfiguration()
} catch { } catch {
print("[SwiftyCam]: Error locking configuration") print("[SwiftyCam]: Error locking configuration")
} }
if pan.state == .ended || pan.state == .failed || pan.state == .cancelled { if pan.state == .ended || pan.state == .failed || pan.state == .cancelled {
previousPanTranslation = 0.0 previousPanTranslation = 0.0
} else { } else {
@ -1199,7 +1222,7 @@ extension SwiftyCamViewController {
doubleTapGesture.numberOfTapsRequired = 2 doubleTapGesture.numberOfTapsRequired = 2
doubleTapGesture.delegate = self doubleTapGesture.delegate = self
previewLayer.addGestureRecognizer(doubleTapGesture) previewLayer.addGestureRecognizer(doubleTapGesture)
panGesture = UIPanGestureRecognizer(target: self, action: #selector(panGesture(pan:))) panGesture = UIPanGestureRecognizer(target: self, action: #selector(panGesture(pan:)))
panGesture.delegate = self panGesture.delegate = self
previewLayer.addGestureRecognizer(panGesture) previewLayer.addGestureRecognizer(panGesture)
@ -1220,7 +1243,3 @@ extension SwiftyCamViewController : UIGestureRecognizerDelegate {
return true return true
} }
} }

View file

@ -15,6 +15,7 @@
import UIKit import UIKit
import AVFoundation
// MARK: Public Protocol Declaration // MARK: Public Protocol Declaration
@ -22,6 +23,24 @@ import UIKit
public protocol SwiftyCamViewControllerDelegate: class { public protocol SwiftyCamViewControllerDelegate: class {
/**
SwiftyCamViewControllerDelegate function called when when SwiftyCamViewController session did start running.
Photos and video capture will be enabled.
- Parameter swiftyCam: Current SwiftyCamViewController
*/
func swiftyCamSessionDidStartRunning(_ swiftyCam: SwiftyCamViewController)
/**
SwiftyCamViewControllerDelegate function called when when SwiftyCamViewController session did stops running.
Photos and video capture will be disabled.
- Parameter swiftyCam: Current SwiftyCamViewController
*/
func swiftyCamSessionDidStopRunning(_ swiftyCam: SwiftyCamViewController)
/** /**
SwiftyCamViewControllerDelegate function called when the takePhoto() function is called. SwiftyCamViewControllerDelegate function called when the takePhoto() function is called.
@ -95,10 +114,34 @@ public protocol SwiftyCamViewControllerDelegate: class {
*/ */
func swiftyCam(_ swiftyCam: SwiftyCamViewController, didChangeZoomLevel zoom: CGFloat) func swiftyCam(_ swiftyCam: SwiftyCamViewController, didChangeZoomLevel zoom: CGFloat)
/**
SwiftyCamViewControllerDelegate function called when when SwiftyCamViewController fails to confiture the session.
- Parameter swiftyCam: Current SwiftyCamViewController
*/
func swiftyCamDidFailToConfigure(_ swiftyCam: SwiftyCamViewController)
/**
SwiftyCamViewControllerDelegate function called when when SwiftyCamViewController does not have access to camera or microphone.
- Parameter swiftyCam: Current SwiftyCamViewController
*/
func swiftyCamNotAuthorized(_ swiftyCam: SwiftyCamViewController)
} }
public extension SwiftyCamViewControllerDelegate { public extension SwiftyCamViewControllerDelegate {
func swiftyCamSessionDidStopRunning(_ swiftyCam: SwiftyCamViewController) {
// Optional
}
func swiftyCamSessionDidStartRunning(_ swiftyCam: SwiftyCamViewController) {
// Optional
}
func swiftyCam(_ swiftyCam: SwiftyCamViewController, didTake photo: UIImage) { func swiftyCam(_ swiftyCam: SwiftyCamViewController, didTake photo: UIImage) {
// Optional // Optional
} }
@ -135,6 +178,14 @@ public extension SwiftyCamViewControllerDelegate {
func swiftyCam(_ swiftyCam: SwiftyCamViewController, didChangeZoomLevel zoom: CGFloat) { func swiftyCam(_ swiftyCam: SwiftyCamViewController, didChangeZoomLevel zoom: CGFloat) {
// Optional // Optional
} }
func swiftyCamDidFailToConfigure(_ swiftyCam: SwiftyCamViewController) {
// Optional
}
func swiftyCamNotAuthorized(_ swiftyCam: SwiftyCamViewController) {
// Optional
}
} }

View file

@ -12,13 +12,6 @@ Pod::Spec.new do |s|
s.summary = 'A Simple, Snapchat inspired camera Framework written in Swift' s.summary = 'A Simple, Snapchat inspired camera Framework written in Swift'
s.ios.deployment_target = '8.0' 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 s.description = <<-DESC
A drop in Camera View Controller for capturing photos and videos from one AVSession. Written in Swift. A drop in Camera View Controller for capturing photos and videos from one AVSession. Written in Swift.
DESC DESC
@ -33,12 +26,4 @@ A drop in Camera View Controller for capturing photos and videos from one AVSess
s.ios.deployment_target = '8.0' s.ios.deployment_target = '8.0'
s.source_files = 'Source/**/*' 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 end