From 42ce13fa387a9451c7f36a7fc3e0d324366ac49e Mon Sep 17 00:00:00 2001 From: Sami Samhuri Date: Thu, 26 Oct 2023 14:47:21 -0700 Subject: [PATCH] Change min deployment target to iOS 17 --- .../DemoSwiftyCam.xcodeproj/project.pbxproj | 10 +- .../DemoSwiftyCam/ViewController.swift | 1 - Source/Orientation.swift | 107 ------------- Source/SwiftyCamViewController.swift | 140 +++++------------- Source/SwiftyCamViewControllerDelegate.swift | 4 +- 5 files changed, 45 insertions(+), 217 deletions(-) delete mode 100644 Source/Orientation.swift diff --git a/DemoSwiftyCam/DemoSwiftyCam.xcodeproj/project.pbxproj b/DemoSwiftyCam/DemoSwiftyCam.xcodeproj/project.pbxproj index 3c22704..f9ac309 100644 --- a/DemoSwiftyCam/DemoSwiftyCam.xcodeproj/project.pbxproj +++ b/DemoSwiftyCam/DemoSwiftyCam.xcodeproj/project.pbxproj @@ -7,8 +7,6 @@ objects = { /* 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, ); }; }; 05D2A9BC1E80BE9D00B479E9 /* PreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1675A9891E00A74A00B80903 /* PreviewView.swift */; }; 05D2A9BD1E80BE9D00B479E9 /* SwiftyCamButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1675A98A1E00A74A00B80903 /* SwiftyCamButton.swift */; }; @@ -28,7 +26,6 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 056AAB081F97CB1700F6A978 /* Orientation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Orientation.swift; path = ../../Source/Orientation.swift; sourceTree = ""; }; 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 = ""; }; 05D2A9B71E80BE9700B479E9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -111,7 +108,6 @@ 1675A9911E00A74F00B80903 /* Source */ = { isa = PBXGroup; children = ( - 056AAB081F97CB1700F6A978 /* Orientation.swift */, 1675A9891E00A74A00B80903 /* PreviewView.swift */, 1675A98A1E00A74A00B80903 /* SwiftyCamButton.swift */, 1675A98B1E00A74A00B80903 /* SwiftyCamViewController.swift */, @@ -236,7 +232,6 @@ buildActionMask = 2147483647; files = ( 05D2A9BC1E80BE9D00B479E9 /* PreviewView.swift in Sources */, - 056AAB0A1F97CB1E00F6A978 /* Orientation.swift in Sources */, 05D2A9BF1E80BE9D00B479E9 /* SwiftyCamViewControllerDelegate.swift in Sources */, 05D2A9BE1E80BE9D00B479E9 /* SwiftyCamViewController.swift in Sources */, 05D2A9BD1E80BE9D00B479E9 /* SwiftyCamButton.swift in Sources */, @@ -252,7 +247,6 @@ 168505E81E288B4C005B4537 /* VideoViewController.swift in Sources */, 1675A9761E00A68300B80903 /* AppDelegate.swift in Sources */, 1675A98F1E00A74A00B80903 /* SwiftyCamViewController.swift in Sources */, - 056AAB091F97CB1700F6A978 /* Orientation.swift in Sources */, 16298B561E2703DC0056D413 /* SwiftyRecordButton.swift in Sources */, 1675A98D1E00A74A00B80903 /* PreviewView.swift in Sources */, 1675A98E1E00A74A00B80903 /* SwiftyCamButton.swift in Sources */, @@ -392,7 +386,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -448,7 +442,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; diff --git a/DemoSwiftyCam/DemoSwiftyCam/ViewController.swift b/DemoSwiftyCam/DemoSwiftyCam/ViewController.swift index e8cf85d..7f8c8da 100644 --- a/DemoSwiftyCam/DemoSwiftyCam/ViewController.swift +++ b/DemoSwiftyCam/DemoSwiftyCam/ViewController.swift @@ -27,7 +27,6 @@ class ViewController: SwiftyCamViewController, SwiftyCamViewControllerDelegate { super.viewDidLoad() shouldPrompToAppSettings = true cameraDelegate = self - shouldUseDeviceOrientation = true allowAutoRotate = true audioEnabled = true flashMode = .auto diff --git a/Source/Orientation.swift b/Source/Orientation.swift deleted file mode 100644 index 68433ab..0000000 --- a/Source/Orientation.swift +++ /dev/null @@ -1,107 +0,0 @@ -/*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(interfaceOrientation: UIInterfaceOrientation) -> AVCaptureVideoOrientation { - // Depends on layout orientation, not device orientation - switch interfaceOrientation { - case .portrait, .unknown: return .portrait - case .landscapeLeft: return .landscapeLeft - case .landscapeRight: return .landscapeRight - case .portraitUpsideDown: return .portraitUpsideDown - @unknown default: return .portrait - } - } - - 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 dd7b758..fdc48bc 100644 --- a/Source/SwiftyCamViewController.swift +++ b/Source/SwiftyCamViewController.swift @@ -34,6 +34,13 @@ open class SwiftyCamViewController: UIViewController { /// Camera on the front of the device case front = "front" + + public var captureDevicePosition: AVCaptureDevice.Position { + switch self { + case .rear: .back + case .front: .front + } + } } public enum FlashMode{ @@ -160,14 +167,6 @@ open class SwiftyCamViewController: UIViewController { public var defaultCamera = CameraSelection.rear - /// Sets wether the taken video should be oriented according to the device orientation - - public var shouldUseDeviceOrientation = false { - didSet { - orientation.shouldUseDeviceOrientation = shouldUseDeviceOrientation - } - } - /// Sets whether or not View Controller supports auto rotation public var allowAutoRotate = false @@ -263,10 +262,6 @@ open class SwiftyCamViewController: UIViewController { fileprivate var previousPanTranslation : CGFloat = 0.0 - /// Last changed orientation - - fileprivate var orientation : Orientation = Orientation() - /// Boolean to store when View Controller is notified session is running fileprivate var sessionRunning = false @@ -277,6 +272,10 @@ open class SwiftyCamViewController: UIViewController { return allowAutoRotate } + open override var supportedInterfaceOrientations: UIInterfaceOrientationMask { + [.portrait] + } + /// Sets output video codec public var videoCodecType: AVVideoCodecType? = nil @@ -327,55 +326,10 @@ open class SwiftyCamViewController: UIViewController { // MARK: ViewDidLayoutSubviews - /// ViewDidLayoutSubviews() Implementation - private func updatePreviewLayer(layer: AVCaptureConnection, orientation: AVCaptureVideoOrientation) { - - if(shouldAutorotate){ - layer.videoOrientation = orientation - } else { - layer.videoOrientation = .portrait - } - - previewLayer.frame = self.view.bounds - - } - override open func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() - if let connection = self.previewLayer?.videoPreviewLayer.connection { - - let currentDevice: UIDevice = UIDevice.current - - let orientation: UIDeviceOrientation = currentDevice.orientation - - let previewLayerConnection : AVCaptureConnection = connection - - if previewLayerConnection.isVideoOrientationSupported { - - switch (orientation) { - case .portrait: updatePreviewLayer(layer: previewLayerConnection, orientation: .portrait) - - break - - case .landscapeRight: updatePreviewLayer(layer: previewLayerConnection, orientation: .landscapeLeft) - - break - - case .landscapeLeft: updatePreviewLayer(layer: previewLayerConnection, orientation: .landscapeRight) - - break - - case .portraitUpsideDown: updatePreviewLayer(layer: previewLayerConnection, orientation: .portraitUpsideDown) - - break - - default: updatePreviewLayer(layer: previewLayerConnection, orientation: .portrait) - - break - } - } - } + previewLayer.frame = view.bounds } // MARK: ViewWillAppear @@ -390,18 +344,21 @@ open class SwiftyCamViewController: UIViewController { // MARK: ViewDidAppear + private var rotationCoordinator: AVCaptureDevice.RotationCoordinator? + + private var rotationAngleObservation: Any? + /// ViewDidAppear(_ animated:) Implementation override open func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - guard let interfaceOrientation = view.window?.windowScene?.interfaceOrientation else { - return - } - // Subscribe to device rotation notifications - - if shouldUseDeviceOrientation { - orientation.start() + if let videoDevice { + rotationCoordinator = AVCaptureDevice.RotationCoordinator(device: videoDevice, previewLayer: previewLayer.videoPreviewLayer) + rotationAngleObservation = rotationCoordinator?.observe(\.videoRotationAngleForHorizonLevelPreview, changeHandler: { [weak self] _, change in + guard let self, let angle = change.newValue else { return } + self.previewLayer.videoPreviewLayer.connection?.videoRotationAngle = angle + }) } // Set background audio preference @@ -415,10 +372,11 @@ open class SwiftyCamViewController: UIViewController { self.session.startRunning() self.isSessionRunning = self.session.isRunning - // Preview layer video orientation can be set only after the connection is created + // Now we can update the video angle on the connection DispatchQueue.main.async { - let layerOrientation = self.orientation.getPreviewLayerOrientation(interfaceOrientation: interfaceOrientation) - self.previewLayer.videoPreviewLayer.connection?.videoOrientation = layerOrientation + if let coordinator = self.rotationCoordinator { + self.previewLayer.videoPreviewLayer.connection?.videoRotationAngle = coordinator.videoRotationAngleForHorizonLevelPreview + } } case .notAuthorized: @@ -427,7 +385,8 @@ open class SwiftyCamViewController: UIViewController { } else { self.cameraDelegate?.swiftyCamNotAuthorized(self) } - case .configurationFailed: + + case .configurationFailed: // Unknown Error DispatchQueue.main.async { self.cameraDelegate?.swiftyCamDidFailToConfigure(self) @@ -457,9 +416,7 @@ open class SwiftyCamViewController: UIViewController { disableFlash() // Unsubscribe from device rotation notifications - if shouldUseDeviceOrientation { - orientation.stop() - } + rotationCoordinator = nil } // MARK: Public Functions @@ -493,25 +450,23 @@ open class SwiftyCamViewController: UIViewController { previewLayer.addSubview(flashView!) } - //Must be fetched before on main thread - let previewOrientation = previewLayer.videoPreviewLayer.connection!.videoOrientation - sessionQueue.async { [unowned self] in if !movieFileOutput.isRecording { if UIDevice.current.isMultitaskingSupported { self.backgroundRecordingID = UIApplication.shared.beginBackgroundTask(expirationHandler: nil) } - // Update the orientation on the movie file output video connection before starting recording. + // Update the angle on the movie file output video connection before starting recording. 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 if self.currentCamera == .front { movieFileOutputConnection?.isVideoMirrored = true } - movieFileOutputConnection?.videoOrientation = self.orientation.getVideoOrientation() ?? previewOrientation + if let rotationCoordinator { + movieFileOutputConnection?.videoRotationAngle = rotationCoordinator.videoRotationAngleForHorizonLevelCapture + } // Start recording to a temporary file. let outputFileName = UUID().uuidString @@ -660,12 +615,7 @@ open class SwiftyCamViewController: UIViewController { /// Add Video Inputs fileprivate func addVideoInput() { - switch currentCamera { - case .front: - videoDevice = SwiftyCamViewController.deviceWithMediaType(AVMediaType.video.rawValue, preferringPosition: .front) - case .rear: - videoDevice = SwiftyCamViewController.deviceWithMediaType(AVMediaType.video.rawValue, preferringPosition: .back) - } + videoDevice = SwiftyCamViewController.defaultCaptureDevice(preferringPosition: currentCamera.captureDevicePosition) if let device = videoDevice { do { @@ -805,9 +755,9 @@ open class SwiftyCamViewController: UIViewController { /// Get Devices - fileprivate class func deviceWithMediaType(_ mediaType: String, preferringPosition position: AVCaptureDevice.Position) -> AVCaptureDevice? { - let avDevice = AVCaptureDevice.default(AVCaptureDevice.DeviceType.builtInWideAngleCamera, for: AVMediaType(rawValue: mediaType), position: position) - return avDevice + fileprivate class func defaultCaptureDevice(preferringPosition position: AVCaptureDevice.Position = .back) -> AVCaptureDevice? { + AVCaptureDevice.default(AVCaptureDevice.DeviceType.builtInTripleCamera, for: .video, position: position) + ?? AVCaptureDevice.default(AVCaptureDevice.DeviceType.builtInWideAngleCamera, for: .video, position: position) } /// Enable flash @@ -948,21 +898,13 @@ extension SwiftyCamViewController { /// Handle pinch gesture - private var firstCaptureDevice: AVCaptureDevice? { - AVCaptureDevice.DiscoverySession( - deviceTypes: [.builtInTripleCamera, .builtInDualCamera, .builtInWideAngleCamera], - mediaType: .video, - position: .unspecified - ).devices.first - } - @objc fileprivate func zoomGesture(pinch: UIPinchGestureRecognizer) { guard pinchToZoom == true && self.currentCamera == .rear else { //ignore pinch return } do { - let captureDevice = firstCaptureDevice + let captureDevice = Self.defaultCaptureDevice() try captureDevice?.lockForConfiguration() zoomScale = min(maxZoomScale, max(1.0, min(beginZoomScale * pinch.scale, captureDevice!.activeFormat.videoMaxZoomFactor))) @@ -1037,7 +979,7 @@ extension SwiftyCamViewController { let translationDifference = currentTranslation - previousPanTranslation do { - let captureDevice = firstCaptureDevice + let captureDevice = Self.defaultCaptureDevice() try captureDevice?.lockForConfiguration() let currentZoom = captureDevice?.videoZoomFactor ?? 0.0 @@ -1106,7 +1048,7 @@ extension SwiftyCamViewController : UIGestureRecognizerDelegate { public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { if gestureRecognizer.isKind(of: UIPinchGestureRecognizer.self) { - beginZoomScale = zoomScale; + beginZoomScale = zoomScale } return true } diff --git a/Source/SwiftyCamViewControllerDelegate.swift b/Source/SwiftyCamViewControllerDelegate.swift index ecd1fe6..614e56d 100644 --- a/Source/SwiftyCamViewControllerDelegate.swift +++ b/Source/SwiftyCamViewControllerDelegate.swift @@ -45,7 +45,7 @@ public protocol SwiftyCamViewControllerDelegate: AnyObject { SwiftyCamViewControllerDelegate function called when SwiftyCamViewController begins recording video. - Parameter swiftyCam: Current SwiftyCamViewController session - - Parameter camera: Current camera orientation + - Parameter camera: Current camera position */ func swiftyCam(_ swiftyCam: SwiftyCamViewController, didBeginRecordingVideo camera: SwiftyCamViewController.CameraSelection) @@ -54,7 +54,7 @@ public protocol SwiftyCamViewControllerDelegate: AnyObject { SwiftyCamViewControllerDelegate function called when SwiftyCamViewController finishes recording video. - Parameter swiftyCam: Current SwiftyCamViewController session - - Parameter camera: Current camera orientation + - Parameter camera: Current camera position */ func swiftyCam(_ swiftyCam: SwiftyCamViewController, didFinishRecordingVideo camera: SwiftyCamViewController.CameraSelection)