diff --git a/Source/Orientation.swift b/Source/Orientation.swift new file mode 100644 index 0000000..f51030e --- /dev/null +++ b/Source/Orientation.swift @@ -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) -> 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 + } + } + + 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 + } + } + } +} + + + diff --git a/Source/SwiftyCamViewController.swift b/Source/SwiftyCamViewController.swift index 4a7e743..42eb596 100644 --- a/Source/SwiftyCamViewController.swift +++ b/Source/SwiftyCamViewController.swift @@ -145,7 +145,11 @@ open class SwiftyCamViewController: UIViewController { /// 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 @@ -245,7 +249,7 @@ open class SwiftyCamViewController: UIViewController { /// Last changed orientation - fileprivate var deviceOrientation : UIDeviceOrientation? + fileprivate var orientation : Orientation = Orientation() /// Boolean to store when View Controller is notified session is running @@ -368,7 +372,7 @@ open class SwiftyCamViewController: UIViewController { // Subscribe to device rotation notifications if shouldUseDeviceOrientation { - subscribeToDeviceOrientationChangeNotifications() + orientation.start() } // Set background audio preference @@ -384,7 +388,7 @@ open class SwiftyCamViewController: UIViewController { // Preview layer video orientation can be set only after the connection is created DispatchQueue.main.async { - self.previewLayer.videoPreviewLayer.connection?.videoOrientation = self.getPreviewLayerOrientation() + self.previewLayer.videoPreviewLayer.connection?.videoOrientation = self.orientation.getPreviewLayerOrientation() } case .notAuthorized: @@ -424,7 +428,7 @@ open class SwiftyCamViewController: UIViewController { // Unsubscribe from device rotation notifications if shouldUseDeviceOrientation { - unsubscribeFromDeviceOrientationChangeNotifications() + orientation.stop() } } @@ -503,6 +507,9 @@ open class SwiftyCamViewController: UIViewController { flashView?.alpha = 0.85 previewLayer.addSubview(flashView!) } + + //Must be fetched before on main thread + let previewOrientation = previewLayer.videoPreviewLayer.connection!.videoOrientation sessionQueue.async { [unowned self] in if !movieFileOutput.isRecording { @@ -519,7 +526,7 @@ open class SwiftyCamViewController: UIViewController { movieFileOutputConnection?.isVideoMirrored = true } - movieFileOutputConnection?.videoOrientation = self.getVideoOrientation() + movieFileOutputConnection?.videoOrientation = self.orientation.getVideoOrientation() ?? previewOrientation // Start recording to a temporary file. let outputFileName = UUID().uuidString @@ -774,67 +781,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. @@ -851,7 +797,7 @@ open class SwiftyCamViewController: UIViewController { // Set proper orientation for photo // 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 }