mirror of
https://github.com/samsonjs/SwiftyCam.git
synced 2026-04-27 15:07:43 +00:00
Added additional documentation and fixed videoquality
This commit is contained in:
parent
dbcf73e18f
commit
44a25d172e
4 changed files with 307 additions and 43 deletions
|
|
@ -16,19 +16,42 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
//MARK: Public Protocol Declaration
|
||||||
|
|
||||||
public protocol SwiftyCamButtonDelegate {
|
public protocol SwiftyCamButtonDelegate {
|
||||||
|
|
||||||
|
// Called when UITapGestureRecognizer begins
|
||||||
|
|
||||||
func buttonWasTapped()
|
func buttonWasTapped()
|
||||||
|
|
||||||
|
// Called When UILongPressGestureRecognizer enters UIGestureRecognizerState.began
|
||||||
|
|
||||||
func buttonDidBeginLongPress()
|
func buttonDidBeginLongPress()
|
||||||
|
|
||||||
|
// Called When UILongPressGestureRecognizer enters UIGestureRecognizerState.end
|
||||||
|
|
||||||
func buttonDidEndLongPress()
|
func buttonDidEndLongPress()
|
||||||
|
|
||||||
|
// Called when the maximum duration is reached
|
||||||
|
|
||||||
func longPressDidReachMaximumDuration()
|
func longPressDidReachMaximumDuration()
|
||||||
|
|
||||||
|
// Sets the maximum duration of the video recording
|
||||||
|
|
||||||
func setMaxiumVideoDuration() -> Double
|
func setMaxiumVideoDuration() -> Double
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Public View Declaration
|
||||||
|
|
||||||
|
|
||||||
open class SwiftyCamButton: UIButton {
|
open class SwiftyCamButton: UIButton {
|
||||||
|
|
||||||
|
// Delegate variable
|
||||||
|
|
||||||
public var delegate: SwiftyCamButtonDelegate?
|
public var delegate: SwiftyCamButtonDelegate?
|
||||||
|
|
||||||
|
// Maximum duration variable
|
||||||
|
|
||||||
fileprivate var timer : Timer?
|
fileprivate var timer : Timer?
|
||||||
|
|
||||||
override public init(frame: CGRect) {
|
override public init(frame: CGRect) {
|
||||||
|
|
@ -41,10 +64,14 @@ open class SwiftyCamButton: UIButton {
|
||||||
createGestureRecognizers()
|
createGestureRecognizers()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UITapGestureRecognizer Function
|
||||||
|
|
||||||
@objc fileprivate func Tap() {
|
@objc fileprivate func Tap() {
|
||||||
self.delegate?.buttonWasTapped()
|
self.delegate?.buttonWasTapped()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UILongPressGestureRecognizer Function
|
||||||
|
|
||||||
@objc fileprivate func LongPress(_ sender:UILongPressGestureRecognizer!) {
|
@objc fileprivate func LongPress(_ sender:UILongPressGestureRecognizer!) {
|
||||||
if (sender.state == UIGestureRecognizerState.ended) {
|
if (sender.state == UIGestureRecognizerState.ended) {
|
||||||
invalidateTimer()
|
invalidateTimer()
|
||||||
|
|
@ -62,17 +89,22 @@ open class SwiftyCamButton: UIButton {
|
||||||
|
|
||||||
fileprivate func startTimer() {
|
fileprivate func startTimer() {
|
||||||
if let duration = delegate?.setMaxiumVideoDuration() {
|
if let duration = delegate?.setMaxiumVideoDuration() {
|
||||||
|
//Check if duration is set, and greater than zero
|
||||||
if duration != 0.0 && duration > 0.0 {
|
if duration != 0.0 && duration > 0.0 {
|
||||||
timer = Timer.scheduledTimer(timeInterval: duration, target: self, selector: #selector(SwiftyCamButton.timerFinished), userInfo: nil, repeats: false)
|
timer = Timer.scheduledTimer(timeInterval: duration, target: self, selector: #selector(SwiftyCamButton.timerFinished), userInfo: nil, repeats: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// End timer if UILongPressGestureRecognizer is ended before time has ended
|
||||||
|
|
||||||
fileprivate func invalidateTimer() {
|
fileprivate func invalidateTimer() {
|
||||||
timer?.invalidate()
|
timer?.invalidate()
|
||||||
timer = nil
|
timer = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add Tap and LongPress gesture recognizers
|
||||||
|
|
||||||
fileprivate func createGestureRecognizers() {
|
fileprivate func createGestureRecognizers() {
|
||||||
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(SwiftyCamButton.Tap))
|
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(SwiftyCamButton.Tap))
|
||||||
let longGesture = UILongPressGestureRecognizer(target: self, action: #selector(SwiftyCamButton.LongPress))
|
let longGesture = UILongPressGestureRecognizer(target: self, action: #selector(SwiftyCamButton.LongPress))
|
||||||
|
|
|
||||||
|
|
@ -17,116 +17,238 @@
|
||||||
import UIKit
|
import UIKit
|
||||||
import AVFoundation
|
import AVFoundation
|
||||||
|
|
||||||
|
// MARK: View Controller Declaration
|
||||||
|
|
||||||
open class SwiftyCamViewController: UIViewController {
|
open class SwiftyCamViewController: UIViewController {
|
||||||
|
|
||||||
|
// MARK: Enumeration Declaration
|
||||||
|
|
||||||
|
|
||||||
|
// Possible Camera Selection Posibilities
|
||||||
|
|
||||||
public enum CameraSelection {
|
public enum CameraSelection {
|
||||||
case rear
|
case rear
|
||||||
case front
|
case front
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used for Setting Video Quality of Capture Session
|
||||||
|
// Corresponds to the AVCaptureSessionPreset String
|
||||||
|
// Global Variables Declared in AVFoundation
|
||||||
|
// AVCaptureSessionPresetPhoto is not supported as it does not support video capture
|
||||||
|
// AVCaptureSessionPreset320x240 is not supported as it is the incorrect aspect ratio
|
||||||
|
|
||||||
public enum VideoQuality {
|
public enum VideoQuality {
|
||||||
case high
|
case high // AVCaptureSessionPresetHigh
|
||||||
case medium
|
case medium // AVCaptureSessionPresetMedium
|
||||||
case low
|
case low // AVCaptureSessionPresetLow
|
||||||
case resolution352x288
|
case resolution352x288 // AVCaptureSessionPreset352x288
|
||||||
case resolution640x480
|
case resolution640x480 // AVCaptureSessionPreset640x480
|
||||||
case resolution1280x720
|
case resolution1280x720 // AVCaptureSessionPreset1280x720
|
||||||
case resolution1920x1080
|
case resolution1920x1080 // AVCaptureSessionPreset1920x1080
|
||||||
case resolution3840x2160
|
case resolution3840x2160 // AVCaptureSessionPreset3840x2160
|
||||||
case iframe960x540
|
case iframe960x540 // AVCaptureSessionPresetiFrame960x540
|
||||||
case iframe1280x720
|
case iframe1280x720 // AVCaptureSessionPresetiFrame1280x720
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Result from the AVCaptureSession Setup
|
||||||
|
|
||||||
fileprivate enum SessionSetupResult {
|
fileprivate enum SessionSetupResult {
|
||||||
case success
|
case success
|
||||||
case notAuthorized
|
case notAuthorized
|
||||||
case configurationFailed
|
case configurationFailed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Public Variable Declarations
|
||||||
|
|
||||||
|
// Public Camera Delegate for the Custom View Controller Subclass
|
||||||
|
|
||||||
public var cameraDelegate: SwiftyCamViewControllerDelegate?
|
public var cameraDelegate: SwiftyCamViewControllerDelegate?
|
||||||
|
|
||||||
|
// Used to set the maximum duration of the video capture
|
||||||
|
// Only used for SwiftyCamButton
|
||||||
|
// Value of 0.0 does not enforce a fixed duration
|
||||||
|
|
||||||
public var kMaximumVideoDuration : Double = 0.0
|
public var kMaximumVideoDuration : Double = 0.0
|
||||||
|
|
||||||
|
// Quality of rear facing camera
|
||||||
|
// Quality of front caing quality will always be VideoQuality.high
|
||||||
|
|
||||||
public var videoQuality : VideoQuality = .high
|
public var videoQuality : VideoQuality = .high
|
||||||
|
|
||||||
|
// Sets whether Pinch to Zoom is supported for the capture session
|
||||||
|
// Pinch to zoom not supported on front facing camera
|
||||||
|
|
||||||
public var pinchToZoom = true
|
public var pinchToZoom = true
|
||||||
|
|
||||||
|
// Sets whether Tap to Focus is supported for the capture session
|
||||||
|
// Tap to Focus is not supported on front facing camera
|
||||||
|
// Tapping the capture session will call the SwiftyCamViewControllerDelegate delegate function SwiftyCamDidFocusAtPoint(focusPoint: CGPoint)
|
||||||
|
|
||||||
public var tapToFocus = true
|
public var tapToFocus = true
|
||||||
|
|
||||||
|
// Sets whether SwiftyCam will prompt a user to the App Settings Screen if Camera or Microphone access is not authorized
|
||||||
|
// If set to false and Camera/Microphone is not authorized, SwiftyCamViewControllerDelegate delegate
|
||||||
|
// function SwiftyCamDidFailCameraPermissionSettings() will be called
|
||||||
|
|
||||||
public var promptToAppPrivacySettings = true
|
public var promptToAppPrivacySettings = true
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Public Get-only Variable Declarations
|
||||||
|
|
||||||
|
// Returns a boolean if the torch (flash) is currently enabled
|
||||||
|
|
||||||
private(set) public var isCameraFlashOn = false
|
private(set) public var isCameraFlashOn = false
|
||||||
|
|
||||||
|
// Returns a boolean if video is currently being recorded
|
||||||
|
|
||||||
private(set) public var isVideRecording = false
|
private(set) public var isVideRecording = false
|
||||||
|
|
||||||
|
// Returns a boolean if the capture session is currently running
|
||||||
|
|
||||||
private(set) public var isSessionRunning = false
|
private(set) public var isSessionRunning = false
|
||||||
|
|
||||||
|
// Returns a CameraSelection enum for the currently utilized camera
|
||||||
|
|
||||||
private(set) public var currentCamera = CameraSelection.rear
|
private(set) public var currentCamera = CameraSelection.rear
|
||||||
|
|
||||||
|
// MARK: Private Constant Declarations
|
||||||
|
|
||||||
|
// Current Capture Session
|
||||||
|
|
||||||
fileprivate let session = AVCaptureSession()
|
fileprivate let session = AVCaptureSession()
|
||||||
|
|
||||||
|
// Serial queue used for setting up session
|
||||||
|
|
||||||
fileprivate let sessionQueue = DispatchQueue(label: "session queue", attributes: [])
|
fileprivate let sessionQueue = DispatchQueue(label: "session queue", attributes: [])
|
||||||
|
|
||||||
|
// MARK: Private Variable Declarations
|
||||||
|
|
||||||
|
// Variable for storing current zoom scale
|
||||||
|
|
||||||
fileprivate var zoomScale = CGFloat(1.0)
|
fileprivate var zoomScale = CGFloat(1.0)
|
||||||
|
|
||||||
|
// Variable for storing initial zoom scale before Pinch to Zoom begins
|
||||||
|
|
||||||
fileprivate var beginZoomScale = CGFloat(1.0)
|
fileprivate var beginZoomScale = CGFloat(1.0)
|
||||||
|
|
||||||
|
// Variable for storing result of Capture Session setup
|
||||||
|
|
||||||
fileprivate var setupResult = SessionSetupResult.success
|
fileprivate var setupResult = SessionSetupResult.success
|
||||||
|
|
||||||
|
// BackgroundID variable for video recording
|
||||||
|
|
||||||
fileprivate var backgroundRecordingID : UIBackgroundTaskIdentifier? = nil
|
fileprivate var backgroundRecordingID : UIBackgroundTaskIdentifier? = nil
|
||||||
|
|
||||||
|
// Video Input variable
|
||||||
|
|
||||||
fileprivate var videoDeviceInput : AVCaptureDeviceInput!
|
fileprivate var videoDeviceInput : AVCaptureDeviceInput!
|
||||||
|
|
||||||
|
// Movie File Output variable
|
||||||
|
|
||||||
fileprivate var movieFileOutput : AVCaptureMovieFileOutput?
|
fileprivate var movieFileOutput : AVCaptureMovieFileOutput?
|
||||||
|
|
||||||
|
// Photo File Output variable
|
||||||
|
|
||||||
fileprivate var photoFileOutput : AVCaptureStillImageOutput?
|
fileprivate var photoFileOutput : AVCaptureStillImageOutput?
|
||||||
|
|
||||||
|
// Video Device variable
|
||||||
|
|
||||||
fileprivate var videoDevice : AVCaptureDevice?
|
fileprivate var videoDevice : AVCaptureDevice?
|
||||||
|
|
||||||
|
// PreviewView for the capture session
|
||||||
|
|
||||||
fileprivate var previewLayer : PreviewView!
|
fileprivate var previewLayer : PreviewView!
|
||||||
|
|
||||||
|
// Disable view autorotation for forced portrait recorindg
|
||||||
|
|
||||||
open override var shouldAutorotate: Bool {
|
open override var shouldAutorotate: Bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: ViewDidLoad
|
||||||
|
|
||||||
override open func viewDidLoad() {
|
override open func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
previewLayer = PreviewView(frame: self.view.frame)
|
previewLayer = PreviewView(frame: self.view.frame)
|
||||||
|
|
||||||
|
// Add Pinch Gesture Recognizer for pinch to zoom
|
||||||
|
|
||||||
addGestureRecognizers(toView: previewLayer)
|
addGestureRecognizers(toView: previewLayer)
|
||||||
self.view.addSubview(previewLayer)
|
self.view.addSubview(previewLayer)
|
||||||
previewLayer.session = session
|
previewLayer.session = session
|
||||||
|
|
||||||
|
// Test authorization status for Camera and Micophone
|
||||||
|
|
||||||
switch AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo){
|
switch AVCaptureDevice.authorizationStatus(forMediaType: AVMediaTypeVideo){
|
||||||
case .authorized:
|
case .authorized:
|
||||||
break
|
|
||||||
case .notDetermined:
|
// already authorized
|
||||||
sessionQueue.suspend()
|
break
|
||||||
AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo, completionHandler: { [unowned self] granted in
|
case .notDetermined:
|
||||||
if !granted {
|
|
||||||
self.setupResult = .notAuthorized
|
// not yet determined
|
||||||
}
|
sessionQueue.suspend()
|
||||||
self.sessionQueue.resume()
|
AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo, completionHandler: { [unowned self] granted in
|
||||||
})
|
if !granted {
|
||||||
default:
|
self.setupResult = .notAuthorized
|
||||||
setupResult = .notAuthorized
|
}
|
||||||
}
|
self.sessionQueue.resume()
|
||||||
sessionQueue.async { [unowned self] in
|
})
|
||||||
self.configureSession()
|
default:
|
||||||
|
|
||||||
|
// already been asked. Denied access
|
||||||
|
setupResult = .notAuthorized
|
||||||
|
}
|
||||||
|
sessionQueue.async { [unowned self] in
|
||||||
|
self.configureSession()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: ViewDidAppear
|
||||||
|
|
||||||
override open func viewDidAppear(_ animated: Bool) {
|
override open func viewDidAppear(_ animated: Bool) {
|
||||||
super.viewDidAppear(animated)
|
super.viewDidAppear(animated)
|
||||||
|
|
||||||
sessionQueue.async {
|
sessionQueue.async {
|
||||||
switch self.setupResult {
|
switch self.setupResult {
|
||||||
case .success:
|
case .success:
|
||||||
self.session.startRunning()
|
// Begin Session
|
||||||
self.isSessionRunning = self.session.isRunning
|
self.session.startRunning()
|
||||||
case .notAuthorized:
|
self.isSessionRunning = self.session.isRunning
|
||||||
self.promptToAppSettings()
|
case .notAuthorized:
|
||||||
case .configurationFailed:
|
// Prompt to App Settings
|
||||||
DispatchQueue.main.async(execute: { [unowned self] in
|
self.promptToAppSettings()
|
||||||
let message = NSLocalizedString("Unable to capture media", comment: "Alert message when something goes wrong during capture session configuration")
|
case .configurationFailed:
|
||||||
let alertController = UIAlertController(title: "AVCam", message: message, preferredStyle: .alert)
|
// Unknown Error
|
||||||
alertController.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "Alert OK button"), style: .cancel, handler: nil))
|
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")
|
||||||
self.present(alertController, animated: true, completion: nil)
|
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)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: ViewDidDisappear
|
||||||
|
|
||||||
override open func viewDidDisappear(_ animated: Bool) {
|
override open func viewDidDisappear(_ animated: Bool) {
|
||||||
super.viewDidDisappear(animated)
|
super.viewDidDisappear(animated)
|
||||||
|
|
||||||
|
// If session is running, stop the session
|
||||||
if self.isSessionRunning == true {
|
if self.isSessionRunning == true {
|
||||||
self.session.stopRunning()
|
self.session.stopRunning()
|
||||||
self.isSessionRunning = false
|
self.isSessionRunning = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Disble flash if it is currently enables
|
||||||
disableFlash()
|
disableFlash()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Public Functions
|
||||||
|
|
||||||
|
// Capture photo from session
|
||||||
|
|
||||||
public func takePhoto() {
|
public func takePhoto() {
|
||||||
if let videoConnection = photoFileOutput?.connection(withMediaType: AVMediaTypeVideo) {
|
if let videoConnection = photoFileOutput?.connection(withMediaType: AVMediaTypeVideo) {
|
||||||
videoConnection.videoOrientation = AVCaptureVideoOrientation.portrait
|
videoConnection.videoOrientation = AVCaptureVideoOrientation.portrait
|
||||||
|
|
@ -134,12 +256,16 @@ open class SwiftyCamViewController: UIViewController {
|
||||||
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
|
||||||
self.cameraDelegate?.SwiftyCamDidTakePhoto(image)
|
self.cameraDelegate?.SwiftyCamDidTakePhoto(image)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Begin recording video
|
||||||
|
|
||||||
public func startVideoRecording() {
|
public func startVideoRecording() {
|
||||||
guard let movieFileOutput = self.movieFileOutput else {
|
guard let movieFileOutput = self.movieFileOutput else {
|
||||||
return
|
return
|
||||||
|
|
@ -176,6 +302,8 @@ open class SwiftyCamViewController: UIViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stop video recording
|
||||||
|
|
||||||
public func endVideoRecording() {
|
public func endVideoRecording() {
|
||||||
if self.movieFileOutput?.isRecording == true {
|
if self.movieFileOutput?.isRecording == true {
|
||||||
self.isVideRecording = false
|
self.isVideRecording = false
|
||||||
|
|
@ -184,8 +312,11 @@ open class SwiftyCamViewController: UIViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Switch between front and rear camera
|
||||||
|
|
||||||
public func switchCamera() {
|
public func switchCamera() {
|
||||||
guard isVideRecording != true else {
|
guard isVideRecording != true else {
|
||||||
|
//TODO: Look into switching camera during video recording
|
||||||
print("[SwiftyCam]: Switching between cameras while recording video is not supported")
|
print("[SwiftyCam]: Switching between cameras while recording video is not supported")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -200,6 +331,8 @@ open class SwiftyCamViewController: UIViewController {
|
||||||
|
|
||||||
sessionQueue.async { [unowned self] in
|
sessionQueue.async { [unowned self] in
|
||||||
|
|
||||||
|
// 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 as! AVCaptureInput)
|
||||||
}
|
}
|
||||||
|
|
@ -211,15 +344,19 @@ open class SwiftyCamViewController: UIViewController {
|
||||||
self.cameraDelegate?.SwiftyCamDidSwitchCameras(camera: self.currentCamera)
|
self.cameraDelegate?.SwiftyCamDidSwitchCameras(camera: self.currentCamera)
|
||||||
self.session.startRunning()
|
self.session.startRunning()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If flash is enabled, disable it as flash is not supported or needed for front facing camera
|
||||||
disableFlash()
|
disableFlash()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func toggleFlash() {
|
public func toggleFlash() {
|
||||||
guard self.currentCamera == .rear else {
|
guard self.currentCamera == .rear else {
|
||||||
|
// Flash is not supported for front facing camera
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
|
let device = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
|
||||||
|
// Check if device has a flash
|
||||||
if (device?.hasTorch)! {
|
if (device?.hasTorch)! {
|
||||||
do {
|
do {
|
||||||
try device?.lockForConfiguration()
|
try device?.lockForConfiguration()
|
||||||
|
|
@ -241,8 +378,12 @@ open class SwiftyCamViewController: UIViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Override Touches Began
|
||||||
|
// Used for Tap to Focus
|
||||||
|
|
||||||
override open func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
|
override open func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||||
guard tapToFocus == true, currentCamera == .rear else {
|
guard tapToFocus == true, currentCamera == .rear else {
|
||||||
|
// Ignore taps
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -257,12 +398,11 @@ open class SwiftyCamViewController: UIViewController {
|
||||||
try device.lockForConfiguration()
|
try device.lockForConfiguration()
|
||||||
|
|
||||||
device.focusPointOfInterest = focusPoint
|
device.focusPointOfInterest = focusPoint
|
||||||
//device.focusMode = .continuousAutoFocus
|
|
||||||
device.focusMode = .autoFocus
|
device.focusMode = .autoFocus
|
||||||
//device.focusMode = .locked
|
|
||||||
device.exposurePointOfInterest = focusPoint
|
device.exposurePointOfInterest = focusPoint
|
||||||
device.exposureMode = AVCaptureExposureMode.continuousAutoExposure
|
device.exposureMode = AVCaptureExposureMode.continuousAutoExposure
|
||||||
device.unlockForConfiguration()
|
device.unlockForConfiguration()
|
||||||
|
//Call delegate function and pass in the location of the touch
|
||||||
self.cameraDelegate?.SwiftyCamDidFocusAtPoint(focusPoint: touchPoint.location(in: previewLayer))
|
self.cameraDelegate?.SwiftyCamDidFocusAtPoint(focusPoint: touchPoint.location(in: previewLayer))
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
|
|
@ -272,7 +412,9 @@ open class SwiftyCamViewController: UIViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**************************************** Private Functions ****************************************/
|
// MARK: Private Functions
|
||||||
|
|
||||||
|
// Configure session, add inputs and outputs
|
||||||
|
|
||||||
fileprivate func configureSession() {
|
fileprivate func configureSession() {
|
||||||
guard setupResult == .success else {
|
guard setupResult == .success else {
|
||||||
|
|
@ -288,6 +430,10 @@ open class SwiftyCamViewController: UIViewController {
|
||||||
session.commitConfiguration()
|
session.commitConfiguration()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Configure preset
|
||||||
|
// Front facing camera will always be set to VideoQuality.high
|
||||||
|
// If set video quality is not supported, videoQuality variable will be set to VideoQuality.high
|
||||||
|
|
||||||
fileprivate func configureVideoPreset() {
|
fileprivate func configureVideoPreset() {
|
||||||
|
|
||||||
if currentCamera == .front {
|
if currentCamera == .front {
|
||||||
|
|
@ -301,6 +447,8 @@ open class SwiftyCamViewController: UIViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add Video Inputs
|
||||||
|
|
||||||
fileprivate func addVideoInput() {
|
fileprivate func addVideoInput() {
|
||||||
switch currentCamera {
|
switch currentCamera {
|
||||||
case .front:
|
case .front:
|
||||||
|
|
@ -353,6 +501,8 @@ open class SwiftyCamViewController: UIViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add Audio Inputs
|
||||||
|
|
||||||
fileprivate func addAudioInput() {
|
fileprivate func addAudioInput() {
|
||||||
do {
|
do {
|
||||||
let audioDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeAudio)
|
let audioDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeAudio)
|
||||||
|
|
@ -370,6 +520,8 @@ open class SwiftyCamViewController: UIViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Configure Movie Output
|
||||||
|
|
||||||
fileprivate func configureVideoOutput() {
|
fileprivate func configureVideoOutput() {
|
||||||
let movieFileOutput = AVCaptureMovieFileOutput()
|
let movieFileOutput = AVCaptureMovieFileOutput()
|
||||||
|
|
||||||
|
|
@ -384,6 +536,8 @@ open class SwiftyCamViewController: UIViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Configure Photo Output
|
||||||
|
|
||||||
fileprivate func configurePhotoOutput() {
|
fileprivate func configurePhotoOutput() {
|
||||||
let photoFileOutput = AVCaptureStillImageOutput()
|
let photoFileOutput = AVCaptureStillImageOutput()
|
||||||
|
|
||||||
|
|
@ -394,12 +548,17 @@ open class SwiftyCamViewController: UIViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get Image from Image Data
|
||||||
|
|
||||||
fileprivate func processPhoto(_ imageData: Data) -> UIImage {
|
fileprivate func processPhoto(_ imageData: Data) -> UIImage {
|
||||||
let dataProvider = CGDataProvider(data: imageData as CFData)
|
let dataProvider = CGDataProvider(data: imageData as CFData)
|
||||||
let cgImageRef = CGImage(jpegDataProviderSource: dataProvider!, decode: nil, shouldInterpolate: true, intent: CGColorRenderingIntent.defaultIntent)
|
let cgImageRef = CGImage(jpegDataProviderSource: dataProvider!, decode: nil, shouldInterpolate: true, intent: CGColorRenderingIntent.defaultIntent)
|
||||||
|
|
||||||
var image: UIImage!
|
var image: UIImage!
|
||||||
|
|
||||||
|
// Set proper orientation for photo
|
||||||
|
// If camera is currently set to front camera, flip image
|
||||||
|
|
||||||
switch self.currentCamera {
|
switch self.currentCamera {
|
||||||
case .front:
|
case .front:
|
||||||
image = UIImage(cgImage: cgImageRef!, scale: 1.0, orientation: .leftMirrored)
|
image = UIImage(cgImage: cgImageRef!, scale: 1.0, orientation: .leftMirrored)
|
||||||
|
|
@ -409,8 +568,11 @@ open class SwiftyCamViewController: UIViewController {
|
||||||
return image
|
return image
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle zoom gesture
|
||||||
|
|
||||||
@objc fileprivate func zoomGesture(pinch: UIPinchGestureRecognizer) {
|
@objc fileprivate func zoomGesture(pinch: UIPinchGestureRecognizer) {
|
||||||
guard pinchToZoom == true else {
|
guard pinchToZoom == true else {
|
||||||
|
//ignore pinch if pinchToZoom is set to false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
|
|
@ -421,6 +583,7 @@ open class SwiftyCamViewController: UIViewController {
|
||||||
|
|
||||||
captureDevice?.videoZoomFactor = zoomScale
|
captureDevice?.videoZoomFactor = zoomScale
|
||||||
|
|
||||||
|
// Call Delegate function with current zoom scale
|
||||||
self.cameraDelegate?.SwiftyCamDidChangeZoomLevel(zoomLevel: zoomScale)
|
self.cameraDelegate?.SwiftyCamDidChangeZoomLevel(zoomLevel: zoomScale)
|
||||||
|
|
||||||
captureDevice?.unlockForConfiguration()
|
captureDevice?.unlockForConfiguration()
|
||||||
|
|
@ -430,17 +593,26 @@ open class SwiftyCamViewController: UIViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add pinch Gesture
|
||||||
|
|
||||||
fileprivate func addGestureRecognizers(toView: UIView) {
|
fileprivate func addGestureRecognizers(toView: UIView) {
|
||||||
let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(zoomGesture(pinch:)))
|
let pinchGesture = UIPinchGestureRecognizer(target: self, action: #selector(zoomGesture(pinch:)))
|
||||||
pinchGesture.delegate = self
|
pinchGesture.delegate = self
|
||||||
toView.addGestureRecognizer(pinchGesture)
|
toView.addGestureRecognizer(pinchGesture)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Handle Denied App Privacy Settings
|
||||||
|
|
||||||
fileprivate func promptToAppSettings() {
|
fileprivate func promptToAppSettings() {
|
||||||
guard promptToAppPrivacySettings == true else {
|
guard promptToAppPrivacySettings == true else {
|
||||||
|
// Do not prompt user
|
||||||
|
// Ca// delegate function SwiftyCamDidFailCameraPermissionSettings()
|
||||||
self.cameraDelegate?.SwiftyCamDidFailCameraPermissionSettings()
|
self.cameraDelegate?.SwiftyCamDidFailCameraPermissionSettings()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// prompt User with UIAlertView
|
||||||
|
|
||||||
DispatchQueue.main.async(execute: { [unowned self] in
|
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 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)
|
let alertController = UIAlertController(title: "AVCam", message: message, preferredStyle: .alert)
|
||||||
|
|
@ -458,6 +630,8 @@ open class SwiftyCamViewController: UIViewController {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set AVCapturePreset from VideoQuality enum
|
||||||
|
|
||||||
fileprivate func videoInputPresetFromVideoQuality(quality: VideoQuality) -> String {
|
fileprivate func videoInputPresetFromVideoQuality(quality: VideoQuality) -> String {
|
||||||
switch quality {
|
switch quality {
|
||||||
case .high: return AVCaptureSessionPresetHigh
|
case .high: return AVCaptureSessionPresetHigh
|
||||||
|
|
@ -475,7 +649,7 @@ open class SwiftyCamViewController: UIViewController {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
print("[SwiftyCam]: Resolution 3840x2160 not supported")
|
print("[SwiftyCam]: Resolution 3840x2160 not supported")
|
||||||
return AVCaptureSessionPresetPhoto
|
return AVCaptureSessionPresetHigh
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -487,12 +661,16 @@ open class SwiftyCamViewController: UIViewController {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enable flash
|
||||||
|
|
||||||
fileprivate func enableFlash() {
|
fileprivate func enableFlash() {
|
||||||
if self.isCameraFlashOn == false {
|
if self.isCameraFlashOn == false {
|
||||||
toggleFlash()
|
toggleFlash()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disable flash
|
||||||
|
|
||||||
fileprivate func disableFlash() {
|
fileprivate func disableFlash() {
|
||||||
if self.isCameraFlashOn == true {
|
if self.isCameraFlashOn == true {
|
||||||
toggleFlash()
|
toggleFlash()
|
||||||
|
|
@ -502,28 +680,43 @@ open class SwiftyCamViewController: UIViewController {
|
||||||
|
|
||||||
extension SwiftyCamViewController : SwiftyCamButtonDelegate {
|
extension SwiftyCamViewController : SwiftyCamButtonDelegate {
|
||||||
|
|
||||||
|
// Sets the maximum duration of the SwiftyCamButton
|
||||||
|
// Value of 0.0 will not enforce any maximum
|
||||||
|
|
||||||
public func setMaxiumVideoDuration() -> Double {
|
public func setMaxiumVideoDuration() -> Double {
|
||||||
return kMaximumVideoDuration
|
return kMaximumVideoDuration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set UITapGesture to take photo
|
||||||
|
|
||||||
public func buttonWasTapped() {
|
public func buttonWasTapped() {
|
||||||
takePhoto()
|
takePhoto()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set UILongPressGesture start to begin video
|
||||||
|
|
||||||
public func buttonDidBeginLongPress() {
|
public func buttonDidBeginLongPress() {
|
||||||
startVideoRecording()
|
startVideoRecording()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set UILongPressGesture begin to begin end video
|
||||||
|
|
||||||
|
|
||||||
public func buttonDidEndLongPress() {
|
public func buttonDidEndLongPress() {
|
||||||
endVideoRecording()
|
endVideoRecording()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Called if maximum duration is reached
|
||||||
|
|
||||||
public func longPressDidReachMaximumDuration() {
|
public func longPressDidReachMaximumDuration() {
|
||||||
endVideoRecording()
|
endVideoRecording()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: AVCaptureFileOutputRecordingDelegate
|
||||||
|
|
||||||
extension SwiftyCamViewController : AVCaptureFileOutputRecordingDelegate {
|
extension SwiftyCamViewController : AVCaptureFileOutputRecordingDelegate {
|
||||||
|
|
||||||
public func capture(_ captureOutput: AVCaptureFileOutput!, didFinishRecordingToOutputFileAt outputFileURL: URL!, fromConnections connections: [Any]!, error: Error!) {
|
public func capture(_ captureOutput: AVCaptureFileOutput!, didFinishRecordingToOutputFileAt outputFileURL: URL!, fromConnections connections: [Any]!, error: Error!) {
|
||||||
if let currentBackgroundRecordingID = backgroundRecordingID {
|
if let currentBackgroundRecordingID = backgroundRecordingID {
|
||||||
backgroundRecordingID = UIBackgroundTaskInvalid
|
backgroundRecordingID = UIBackgroundTaskInvalid
|
||||||
|
|
@ -535,12 +728,18 @@ extension SwiftyCamViewController : AVCaptureFileOutputRecordingDelegate {
|
||||||
if error != nil {
|
if error != nil {
|
||||||
print("[SwiftyCam]: Movie file finishing error: \(error)")
|
print("[SwiftyCam]: Movie file finishing error: \(error)")
|
||||||
} else {
|
} else {
|
||||||
|
//Call delegate function with the URL of the outputfile
|
||||||
self.cameraDelegate?.SwiftyCamDidFinishProcessingVideoAt(outputFileURL)
|
self.cameraDelegate?.SwiftyCamDidFinishProcessingVideoAt(outputFileURL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: UIGestureRecognizerDelegate
|
||||||
|
|
||||||
extension SwiftyCamViewController : UIGestureRecognizerDelegate {
|
extension SwiftyCamViewController : UIGestureRecognizerDelegate {
|
||||||
|
|
||||||
|
// Set beginZoomScale when pinch begins
|
||||||
|
|
||||||
public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||||
if gestureRecognizer.isKind(of: UIPinchGestureRecognizer.self) {
|
if gestureRecognizer.isKind(of: UIPinchGestureRecognizer.self) {
|
||||||
beginZoomScale = zoomScale;
|
beginZoomScale = zoomScale;
|
||||||
|
|
|
||||||
|
|
@ -16,14 +16,47 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
// MARK: Public Protocol Declaration
|
||||||
|
|
||||||
public protocol SwiftyCamViewControllerDelegate {
|
public protocol SwiftyCamViewControllerDelegate {
|
||||||
|
|
||||||
|
// Called when SwiftyCamViewController takes a photo. Returns a UIImage
|
||||||
|
|
||||||
func SwiftyCamDidTakePhoto(_ photo:UIImage)
|
func SwiftyCamDidTakePhoto(_ photo:UIImage)
|
||||||
|
|
||||||
|
// Called when SwiftyCamViewController begins recording video
|
||||||
|
|
||||||
func SwiftyCamDidBeginRecordingVideo()
|
func SwiftyCamDidBeginRecordingVideo()
|
||||||
|
|
||||||
|
// Called when SwiftyCamViewController finishes recording video
|
||||||
|
|
||||||
func SwiftyCamDidFinishRecordingVideo()
|
func SwiftyCamDidFinishRecordingVideo()
|
||||||
|
|
||||||
|
// Called when SwiftyCamViewController is done processing video. Returns the URL of the video location
|
||||||
|
|
||||||
func SwiftyCamDidFinishProcessingVideoAt(_ url: URL)
|
func SwiftyCamDidFinishProcessingVideoAt(_ url: URL)
|
||||||
|
|
||||||
|
// Called when SwiftyCamViewController switches between front or rear camera. Return the current CameraSelection
|
||||||
|
|
||||||
func SwiftyCamDidSwitchCameras(camera: SwiftyCamViewController.CameraSelection)
|
func SwiftyCamDidSwitchCameras(camera: SwiftyCamViewController.CameraSelection)
|
||||||
|
|
||||||
|
// Called when SwiftyCamViewController view is tapped and begins focusing at that point
|
||||||
|
// Will only be called if tapToFocus is set to true
|
||||||
|
// Not supported on front facing camera
|
||||||
|
// Returns the CGPoint tap location
|
||||||
|
|
||||||
func SwiftyCamDidFocusAtPoint(focusPoint: CGPoint)
|
func SwiftyCamDidFocusAtPoint(focusPoint: CGPoint)
|
||||||
|
|
||||||
|
// Called when SwiftyCamViewController view changes zoom level
|
||||||
|
// Will only be called if pinchToZoom is set to true
|
||||||
|
// Not supported on front facing camera
|
||||||
|
// Returns the current zoomLevel
|
||||||
|
|
||||||
func SwiftyCamDidChangeZoomLevel(zoomLevel: CGFloat)
|
func SwiftyCamDidChangeZoomLevel(zoomLevel: CGFloat)
|
||||||
|
|
||||||
|
// Called if app permissions are denied for either Camera or Microphone
|
||||||
|
// Only called if promptToAppPrivacySettings is set to false
|
||||||
|
|
||||||
func SwiftyCamDidFailCameraPermissionSettings()
|
func SwiftyCamDidFailCameraPermissionSettings()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
Pod::Spec.new do |s|
|
Pod::Spec.new do |s|
|
||||||
s.name = 'SwiftyCam'
|
s.name = 'SwiftyCam'
|
||||||
s.version = '1.1.4'
|
s.version = '1.1.5'
|
||||||
s.summary = 'A Simple, Snapchat-style camera Framework written in Swift'
|
s.summary = 'A Simple, Snapchat-style camera Framework written in Swift'
|
||||||
s.ios.deployment_target = '8.0'
|
s.ios.deployment_target = '8.0'
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue