mirror of
				https://github.com/yattee/yattee.git
				synced 2025-11-04 06:32:03 +00:00 
			
		
		
		
	Orientation improvements
This commit is contained in:
		@@ -92,7 +92,6 @@ extension Defaults.Keys {
 | 
			
		||||
    #if os(iOS)
 | 
			
		||||
        static let honorSystemOrientationLock = Key<Bool>("honorSystemOrientationLock", default: true)
 | 
			
		||||
        static let enterFullscreenInLandscape = Key<Bool>("enterFullscreenInLandscape", default: UIDevice.current.userInterfaceIdiom == .phone)
 | 
			
		||||
        static let lockOrientationInFullScreen = Key<Bool>("lockOrientationInFullScreen", default: false)
 | 
			
		||||
    #endif
 | 
			
		||||
 | 
			
		||||
    static let showMPVPlaybackStats = Key<Bool>("showMPVPlaybackStats", default: false)
 | 
			
		||||
 
 | 
			
		||||
@@ -136,40 +136,13 @@ extension AppleAVPlayerViewController: AVPlayerViewControllerDelegate {
 | 
			
		||||
 | 
			
		||||
    func playerViewController(
 | 
			
		||||
        _: AVPlayerViewController,
 | 
			
		||||
        willBeginFullScreenPresentationWithAnimationCoordinator context: UIViewControllerTransitionCoordinator
 | 
			
		||||
    ) {
 | 
			
		||||
        #if os(iOS)
 | 
			
		||||
            if !context.isCancelled, Defaults[.lockOrientationInFullScreen] {
 | 
			
		||||
                Orientation.lockOrientation(.landscape, andRotateTo: UIDevice.current.orientation.isLandscape ? nil : .landscapeRight)
 | 
			
		||||
            }
 | 
			
		||||
        #endif
 | 
			
		||||
    }
 | 
			
		||||
        willBeginFullScreenPresentationWithAnimationCoordinator _: UIViewControllerTransitionCoordinator
 | 
			
		||||
    ) {}
 | 
			
		||||
 | 
			
		||||
    func playerViewController(
 | 
			
		||||
        _: AVPlayerViewController,
 | 
			
		||||
        willEndFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator
 | 
			
		||||
    ) {
 | 
			
		||||
        let wasPlaying = playerModel.isPlaying
 | 
			
		||||
        coordinator.animate(alongsideTransition: nil) { context in
 | 
			
		||||
            #if os(iOS)
 | 
			
		||||
                if wasPlaying {
 | 
			
		||||
                    self.playerModel.play()
 | 
			
		||||
                }
 | 
			
		||||
            #endif
 | 
			
		||||
            if !context.isCancelled {
 | 
			
		||||
                #if os(iOS)
 | 
			
		||||
                    self.playerModel.lockedOrientation = nil
 | 
			
		||||
                    if Defaults[.enterFullscreenInLandscape] {
 | 
			
		||||
                        Orientation.lockOrientation(.portrait, andRotateTo: .portrait)
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if wasPlaying {
 | 
			
		||||
                        self.playerModel.play()
 | 
			
		||||
                    }
 | 
			
		||||
                #endif
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
        willEndFullScreenPresentationWithAnimationCoordinator _: UIViewControllerTransitionCoordinator
 | 
			
		||||
    ) {}
 | 
			
		||||
 | 
			
		||||
    func playerViewController(
 | 
			
		||||
        _: AVPlayerViewController,
 | 
			
		||||
 
 | 
			
		||||
@@ -38,7 +38,6 @@ struct VideoPlayerView: View {
 | 
			
		||||
    #if os(iOS)
 | 
			
		||||
        @Environment(\.verticalSizeClass) private var verticalSizeClass
 | 
			
		||||
 | 
			
		||||
        @State private var motionManager: CMMotionManager!
 | 
			
		||||
        @State private var orientation = UIInterfaceOrientation.portrait
 | 
			
		||||
        @State private var lastOrientation: UIInterfaceOrientation?
 | 
			
		||||
    #elseif os(macOS)
 | 
			
		||||
@@ -94,7 +93,7 @@ struct VideoPlayerView: View {
 | 
			
		||||
                            playerSize = geometry.size
 | 
			
		||||
                        }
 | 
			
		||||
                }
 | 
			
		||||
//                .ignoresSafeArea(.all, edges: playerEdgesIgnoringSafeArea)
 | 
			
		||||
                .ignoresSafeArea(.all, edges: playerEdgesIgnoringSafeArea)
 | 
			
		||||
                .onChange(of: geometry.size) { size in
 | 
			
		||||
                    self.playerSize = size
 | 
			
		||||
                }
 | 
			
		||||
@@ -102,9 +101,6 @@ struct VideoPlayerView: View {
 | 
			
		||||
                    player.backend.setNeedsDrawing(!value)
 | 
			
		||||
                }
 | 
			
		||||
                #if os(iOS)
 | 
			
		||||
                .onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
 | 
			
		||||
                    handleOrientationDidChangeNotification()
 | 
			
		||||
                }
 | 
			
		||||
                .onChange(of: player.presentingPlayer) { newValue in
 | 
			
		||||
                    if newValue {
 | 
			
		||||
                        viewVerticalOffset = 0
 | 
			
		||||
@@ -120,9 +116,6 @@ struct VideoPlayerView: View {
 | 
			
		||||
                        } else {
 | 
			
		||||
                            Orientation.lockOrientation(.allButUpsideDown)
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        motionManager?.stopAccelerometerUpdates()
 | 
			
		||||
                        motionManager = nil
 | 
			
		||||
                        viewVerticalOffset = Self.hiddenOffset
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
@@ -203,7 +196,6 @@ struct VideoPlayerView: View {
 | 
			
		||||
                                #endif
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
//                        .ignoresSafeArea(.all, edges: fullScreenLayout ? .bottom : Edge.Set())
 | 
			
		||||
                        .frame(maxWidth: fullScreenLayout ? .infinity : nil, maxHeight: fullScreenLayout ? .infinity : nil)
 | 
			
		||||
                        .onHover { hovering in
 | 
			
		||||
                            hoveringPlayer = hovering
 | 
			
		||||
@@ -253,7 +245,7 @@ struct VideoPlayerView: View {
 | 
			
		||||
                                .background(Color.black)
 | 
			
		||||
 | 
			
		||||
                        #if !os(tvOS)
 | 
			
		||||
                            if !player.playingFullScreen {
 | 
			
		||||
                            if !fullScreenLayout {
 | 
			
		||||
                                VStack(spacing: 0) {
 | 
			
		||||
                                    #if os(iOS)
 | 
			
		||||
                                        VideoDetails(sidebarQueue: sidebarQueue, fullScreen: $fullScreenDetails)
 | 
			
		||||
@@ -281,7 +273,7 @@ struct VideoPlayerView: View {
 | 
			
		||||
            #if os(macOS)
 | 
			
		||||
                .frame(minWidth: 650)
 | 
			
		||||
            #endif
 | 
			
		||||
            if !player.playingFullScreen {
 | 
			
		||||
            if !fullScreenLayout {
 | 
			
		||||
                #if os(iOS)
 | 
			
		||||
                    if sidebarQueue {
 | 
			
		||||
                        PlayerQueueView(sidebarQueue: true, fullScreen: $fullScreenDetails)
 | 
			
		||||
@@ -297,7 +289,7 @@ struct VideoPlayerView: View {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        #if os(iOS)
 | 
			
		||||
        .statusBar(hidden: player.playingFullScreen)
 | 
			
		||||
        .statusBar(hidden: fullScreenLayout)
 | 
			
		||||
        #endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -339,18 +331,26 @@ struct VideoPlayerView: View {
 | 
			
		||||
                PlayerGestures()
 | 
			
		||||
                PlayerControls(player: player, thumbnails: thumbnails)
 | 
			
		||||
                #if os(iOS)
 | 
			
		||||
                    .padding(.top, fullScreenLayout ? (safeAreaInsets.top.isZero ? safeAreaInsets.bottom : safeAreaInsets.top) : 0)
 | 
			
		||||
                    .padding(.top, controlsTopPadding)
 | 
			
		||||
                    .padding(.bottom, fullScreenLayout ? safeAreaInsets.bottom : 0)
 | 
			
		||||
                #endif
 | 
			
		||||
            #endif
 | 
			
		||||
        }
 | 
			
		||||
        .ignoresSafeArea(.all, edges: fullScreenLayout ? .vertical : Edge.Set())
 | 
			
		||||
        #if os(iOS)
 | 
			
		||||
            .statusBarHidden(fullScreenLayout)
 | 
			
		||||
        .statusBarHidden(fullScreenLayout)
 | 
			
		||||
        #endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #if os(iOS)
 | 
			
		||||
        var controlsTopPadding: Double {
 | 
			
		||||
            guard fullScreenLayout else { return 0 }
 | 
			
		||||
 | 
			
		||||
            let idiom = UIDevice.current.userInterfaceIdiom
 | 
			
		||||
            guard idiom == .pad else { return safeAreaInsets.top }
 | 
			
		||||
 | 
			
		||||
            return safeAreaInsets.top.isZero ? safeAreaInsets.bottom : safeAreaInsets.top
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var safeAreaInsets: UIEdgeInsets {
 | 
			
		||||
            UIApplication.shared.windows.first?.safeAreaInsets ?? .init()
 | 
			
		||||
        }
 | 
			
		||||
@@ -432,7 +432,7 @@ struct VideoPlayerView: View {
 | 
			
		||||
 | 
			
		||||
    #if os(iOS)
 | 
			
		||||
        private func configureOrientationUpdatesBasedOnAccelerometer() {
 | 
			
		||||
            if UIDevice.current.orientation.isLandscape,
 | 
			
		||||
            if OrientationTracker.shared.currentInterfaceOrientation.isLandscape,
 | 
			
		||||
               Defaults[.enterFullscreenInLandscape],
 | 
			
		||||
               !player.playingFullScreen,
 | 
			
		||||
               !player.playingInPictureInPicture
 | 
			
		||||
@@ -442,32 +442,16 @@ struct VideoPlayerView: View {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            guard !Defaults[.honorSystemOrientationLock], motionManager.isNil else {
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            motionManager = CMMotionManager()
 | 
			
		||||
            motionManager.accelerometerUpdateInterval = 0.2
 | 
			
		||||
            motionManager.startAccelerometerUpdates(to: OperationQueue()) { data, _ in
 | 
			
		||||
                guard player.presentingPlayer, !player.playingInPictureInPicture, !data.isNil else {
 | 
			
		||||
            NotificationCenter.default.addObserver(
 | 
			
		||||
                forName: OrientationTracker.deviceOrientationChangedNotification,
 | 
			
		||||
                object: nil,
 | 
			
		||||
                queue: .main
 | 
			
		||||
            ) { _ in
 | 
			
		||||
                guard !Defaults[.honorSystemOrientationLock], player.presentingPlayer, !player.playingInPictureInPicture else {
 | 
			
		||||
                    return
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                guard let acceleration = data?.acceleration else {
 | 
			
		||||
                    return
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var orientation = UIInterfaceOrientation.unknown
 | 
			
		||||
 | 
			
		||||
                if acceleration.x >= 0.65 {
 | 
			
		||||
                    orientation = .landscapeLeft
 | 
			
		||||
                } else if acceleration.x <= -0.65 {
 | 
			
		||||
                    orientation = .landscapeRight
 | 
			
		||||
                } else if acceleration.y <= -0.65 {
 | 
			
		||||
                    orientation = .portrait
 | 
			
		||||
                } else if acceleration.y >= 0.65 {
 | 
			
		||||
                    orientation = .portraitUpsideDown
 | 
			
		||||
                }
 | 
			
		||||
                let orientation = OrientationTracker.shared.currentInterfaceOrientation
 | 
			
		||||
 | 
			
		||||
                guard lastOrientation != orientation else {
 | 
			
		||||
                    return
 | 
			
		||||
@@ -475,67 +459,21 @@ struct VideoPlayerView: View {
 | 
			
		||||
 | 
			
		||||
                lastOrientation = orientation
 | 
			
		||||
 | 
			
		||||
                if orientation.isLandscape {
 | 
			
		||||
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.8) {
 | 
			
		||||
                        guard Defaults[.enterFullscreenInLandscape] else {
 | 
			
		||||
                            return
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        player.enterFullScreen()
 | 
			
		||||
 | 
			
		||||
                        let orientationLockMask = orientation == .landscapeLeft ?
 | 
			
		||||
                            UIInterfaceOrientationMask.landscapeLeft : .landscapeRight
 | 
			
		||||
 | 
			
		||||
                        Orientation.lockOrientation(orientationLockMask, andRotateTo: orientation)
 | 
			
		||||
 | 
			
		||||
                        guard Defaults[.lockOrientationInFullScreen] else {
 | 
			
		||||
                            return
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        player.lockedOrientation = orientation
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    guard abs(acceleration.z) <= 0.74,
 | 
			
		||||
                          player.lockedOrientation.isNil,
 | 
			
		||||
                          Defaults[.enterFullscreenInLandscape],
 | 
			
		||||
                          !Defaults[.lockOrientationInFullScreen]
 | 
			
		||||
                    else {
 | 
			
		||||
                DispatchQueue.main.async {
 | 
			
		||||
                    guard Defaults[.enterFullscreenInLandscape] else {
 | 
			
		||||
                        return
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    Orientation.lockOrientation(.portrait)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private func handleOrientationDidChangeNotification() {
 | 
			
		||||
            viewVerticalOffset = viewVerticalOffset == 0 ? 0 : Self.hiddenOffset
 | 
			
		||||
            let newOrientation = UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
 | 
			
		||||
            if newOrientation?.isLandscape ?? false,
 | 
			
		||||
               player.presentingPlayer,
 | 
			
		||||
               Defaults[.lockOrientationInFullScreen],
 | 
			
		||||
               !player.lockedOrientation.isNil
 | 
			
		||||
            {
 | 
			
		||||
                Orientation.lockOrientation(.landscape, andRotateTo: newOrientation)
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            guard player.presentingPlayer, Defaults[.enterFullscreenInLandscape], Defaults[.honorSystemOrientationLock] else {
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if UIDevice.current.orientation.isLandscape {
 | 
			
		||||
                DispatchQueue.main.async {
 | 
			
		||||
                    player.lockedOrientation = newOrientation
 | 
			
		||||
                    player.enterFullScreen()
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                DispatchQueue.main.async {
 | 
			
		||||
                    player.exitFullScreen()
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.8) {
 | 
			
		||||
                    player.exitFullScreen()
 | 
			
		||||
                    if orientation.isLandscape {
 | 
			
		||||
                        player.enterFullScreen()
 | 
			
		||||
                        Orientation.lockOrientation(OrientationTracker.shared.currentInterfaceOrientationMask, andRotateTo: orientation)
 | 
			
		||||
                    } else {
 | 
			
		||||
                        if !player.playingFullScreen {
 | 
			
		||||
                            player.exitFullScreen()
 | 
			
		||||
                        } else {
 | 
			
		||||
                            Orientation.lockOrientation(.allButUpsideDown, andRotateTo: .portrait)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,6 @@ struct PlayerSettings: View {
 | 
			
		||||
    @Default(.closeLastItemOnPlaybackEnd) private var closeLastItemOnPlaybackEnd
 | 
			
		||||
    #if os(iOS)
 | 
			
		||||
        @Default(.honorSystemOrientationLock) private var honorSystemOrientationLock
 | 
			
		||||
        @Default(.lockOrientationInFullScreen) private var lockOrientationInFullScreen
 | 
			
		||||
        @Default(.enterFullscreenInLandscape) private var enterFullscreenInLandscape
 | 
			
		||||
    #endif
 | 
			
		||||
    @Default(.closePiPOnNavigation) private var closePiPOnNavigation
 | 
			
		||||
@@ -93,7 +92,6 @@ struct PlayerSettings: View {
 | 
			
		||||
                        enterFullscreenInLandscapeToggle
 | 
			
		||||
                    }
 | 
			
		||||
                    honorSystemOrientationLockToggle
 | 
			
		||||
                    lockOrientationInFullScreenToggle
 | 
			
		||||
                }
 | 
			
		||||
            #endif
 | 
			
		||||
        }
 | 
			
		||||
@@ -186,11 +184,6 @@ struct PlayerSettings: View {
 | 
			
		||||
        private var enterFullscreenInLandscapeToggle: some View {
 | 
			
		||||
            Toggle("Enter fullscreen in landscape", isOn: $enterFullscreenInLandscape)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private var lockOrientationInFullScreenToggle: some View {
 | 
			
		||||
            Toggle("Lock orientation in fullscreen", isOn: $lockOrientationInFullScreen)
 | 
			
		||||
                .disabled(!enterFullscreenInLandscape)
 | 
			
		||||
        }
 | 
			
		||||
    #endif
 | 
			
		||||
 | 
			
		||||
    private var closePiPOnNavigationToggle: some View {
 | 
			
		||||
 
 | 
			
		||||
@@ -511,6 +511,7 @@
 | 
			
		||||
		379775932689365600DD52A8 /* Array+Next.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379775922689365600DD52A8 /* Array+Next.swift */; };
 | 
			
		||||
		379775942689365600DD52A8 /* Array+Next.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379775922689365600DD52A8 /* Array+Next.swift */; };
 | 
			
		||||
		379775952689365600DD52A8 /* Array+Next.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379775922689365600DD52A8 /* Array+Next.swift */; };
 | 
			
		||||
		379B0253287A1CDF001015B5 /* OrientationTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379B0252287A1CDF001015B5 /* OrientationTracker.swift */; };
 | 
			
		||||
		37A3B15A27255E7F000FB5EE /* SafariWebExtensionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A3B15927255E7F000FB5EE /* SafariWebExtensionHandler.swift */; };
 | 
			
		||||
		37A3B15F27255E7F000FB5EE /* images in Resources */ = {isa = PBXBuildFile; fileRef = 37A3B15E27255E7F000FB5EE /* images */; };
 | 
			
		||||
		37A3B16127255E7F000FB5EE /* manifest.json in Resources */ = {isa = PBXBuildFile; fileRef = 37A3B16027255E7F000FB5EE /* manifest.json */; };
 | 
			
		||||
@@ -1073,6 +1074,7 @@
 | 
			
		||||
		3797758A2689345500DD52A8 /* Store.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Store.swift; sourceTree = "<group>"; };
 | 
			
		||||
		379775922689365600DD52A8 /* Array+Next.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Next.swift"; sourceTree = "<group>"; };
 | 
			
		||||
		37992DC726CC50BC003D4C27 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 | 
			
		||||
		379B0252287A1CDF001015B5 /* OrientationTracker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrientationTracker.swift; sourceTree = "<group>"; };
 | 
			
		||||
		37A3B15727255E7F000FB5EE /* Open in Yattee - macOS.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Open in Yattee - macOS.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
 | 
			
		||||
		37A3B15927255E7F000FB5EE /* SafariWebExtensionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariWebExtensionHandler.swift; sourceTree = "<group>"; };
 | 
			
		||||
		37A3B15E27255E7F000FB5EE /* images */ = {isa = PBXFileReference; lastKnownFileType = folder; path = images; sourceTree = "<group>"; };
 | 
			
		||||
@@ -1775,6 +1777,7 @@
 | 
			
		||||
			children = (
 | 
			
		||||
				37B4E802277D0A72004BF56A /* AppDelegate.swift */,
 | 
			
		||||
				37B4E804277D0AB4004BF56A /* Orientation.swift */,
 | 
			
		||||
				379B0252287A1CDF001015B5 /* OrientationTracker.swift */,
 | 
			
		||||
				3784B23A272894DA00B09468 /* ShareSheet.swift */,
 | 
			
		||||
				3749BF9227ADA142000480FF /* BridgingHeader.h */,
 | 
			
		||||
				37992DC726CC50BC003D4C27 /* Info.plist */,
 | 
			
		||||
@@ -2832,6 +2835,7 @@
 | 
			
		||||
				37E70923271CD43000D34DDE /* WelcomeScreen.swift in Sources */,
 | 
			
		||||
				37BD07BB2698AB60003EBB87 /* AppSidebarNavigation.swift in Sources */,
 | 
			
		||||
				37C0697A2725C09E00F7F6CB /* PlayerQueueItemBridge.swift in Sources */,
 | 
			
		||||
				379B0253287A1CDF001015B5 /* OrientationTracker.swift in Sources */,
 | 
			
		||||
				370B79C9286279810045DB77 /* NSObject+Swizzle.swift in Sources */,
 | 
			
		||||
				37D4B0E42671614900C925CA /* YatteeApp.swift in Sources */,
 | 
			
		||||
				37C3A241272359900087A57A /* Double+Format.swift in Sources */,
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,8 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
 | 
			
		||||
            UITabBar.appearance().backgroundImage = UIImage()
 | 
			
		||||
            UITabBar.appearance().isTranslucent = true
 | 
			
		||||
            UITabBar.appearance().backgroundColor = .clear
 | 
			
		||||
 | 
			
		||||
            OrientationTracker.shared.startDeviceOrientationTracking()
 | 
			
		||||
        #endif
 | 
			
		||||
        return true
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -21,11 +21,16 @@ struct Orientation {
 | 
			
		||||
    static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation: UIInterfaceOrientation? = nil) {
 | 
			
		||||
        lockOrientation(orientation)
 | 
			
		||||
 | 
			
		||||
        guard !rotateOrientation.isNil else {
 | 
			
		||||
        guard let rotateOrientation = rotateOrientation else {
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        UIDevice.current.setValue(rotateOrientation!.rawValue, forKey: "orientation")
 | 
			
		||||
        let orientationString = rotateOrientation == .portrait ? "portrait" : rotateOrientation == .landscapeLeft ? "landscapeLeft" :
 | 
			
		||||
            rotateOrientation == .landscapeRight ? "landscapeRight" : rotateOrientation == .portraitUpsideDown ? "portraitUpsideDown" : "allButUpsideDown"
 | 
			
		||||
 | 
			
		||||
        logger.info("rotating to \(orientationString)")
 | 
			
		||||
 | 
			
		||||
        UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation")
 | 
			
		||||
        UINavigationController.attemptRotationToDeviceOrientation()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										92
									
								
								iOS/OrientationTracker.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								iOS/OrientationTracker.swift
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,92 @@
 | 
			
		||||
import CoreMotion
 | 
			
		||||
import UIKit
 | 
			
		||||
 | 
			
		||||
public class OrientationTracker {
 | 
			
		||||
    public static let shared = OrientationTracker()
 | 
			
		||||
 | 
			
		||||
    public static let deviceOrientationChangedNotification = NSNotification.Name("DeviceOrientationChangedNotification")
 | 
			
		||||
 | 
			
		||||
    public var currentDeviceOrientation: UIDeviceOrientation = .portrait
 | 
			
		||||
 | 
			
		||||
    public var currentInterfaceOrientation: UIInterfaceOrientation {
 | 
			
		||||
        switch currentDeviceOrientation {
 | 
			
		||||
        case .landscapeLeft:
 | 
			
		||||
            return .landscapeLeft
 | 
			
		||||
        case .landscapeRight:
 | 
			
		||||
            return .landscapeRight
 | 
			
		||||
        default:
 | 
			
		||||
            return .portrait
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public var currentInterfaceOrientationMask: UIInterfaceOrientationMask {
 | 
			
		||||
        switch currentInterfaceOrientation {
 | 
			
		||||
        case .landscapeLeft:
 | 
			
		||||
            return .landscapeLeft
 | 
			
		||||
        case .landscapeRight:
 | 
			
		||||
            return .landscapeRight
 | 
			
		||||
        default:
 | 
			
		||||
            return .portrait
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public var affineTransform: CGAffineTransform {
 | 
			
		||||
        var angleRadians: Double
 | 
			
		||||
        switch currentDeviceOrientation {
 | 
			
		||||
        case .portrait:
 | 
			
		||||
            angleRadians = 0
 | 
			
		||||
        case .landscapeLeft:
 | 
			
		||||
            angleRadians = -0.5 * .pi
 | 
			
		||||
        case .landscapeRight:
 | 
			
		||||
            angleRadians = 0.5 * .pi
 | 
			
		||||
        case .portraitUpsideDown:
 | 
			
		||||
            angleRadians = .pi
 | 
			
		||||
        default:
 | 
			
		||||
            return .identity
 | 
			
		||||
        }
 | 
			
		||||
        return CGAffineTransform(rotationAngle: angleRadians)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private let motionManager: CMMotionManager
 | 
			
		||||
    private let queue: OperationQueue
 | 
			
		||||
 | 
			
		||||
    private init() {
 | 
			
		||||
        motionManager = CMMotionManager()
 | 
			
		||||
        motionManager.accelerometerUpdateInterval = 0.1
 | 
			
		||||
        queue = OperationQueue()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public func startDeviceOrientationTracking() {
 | 
			
		||||
        motionManager.startAccelerometerUpdates(to: queue) { accelerometerData, error in
 | 
			
		||||
            guard error == nil else { return }
 | 
			
		||||
            guard let accelerometerData = accelerometerData else { return }
 | 
			
		||||
 | 
			
		||||
            let newDeviceOrientation = self.deviceOrientation(forAccelerometerData: accelerometerData)
 | 
			
		||||
            guard newDeviceOrientation != self.currentDeviceOrientation else { return }
 | 
			
		||||
            self.currentDeviceOrientation = newDeviceOrientation
 | 
			
		||||
 | 
			
		||||
            NotificationCenter.default.post(name: Self.deviceOrientationChangedNotification,
 | 
			
		||||
                                            object: nil,
 | 
			
		||||
                                            userInfo: nil)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public func stopDeviceOrientationTracking() {
 | 
			
		||||
        motionManager.stopAccelerometerUpdates()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private func deviceOrientation(forAccelerometerData accelerometerData: CMAccelerometerData) -> UIDeviceOrientation {
 | 
			
		||||
        let treshold = 0.55
 | 
			
		||||
        if accelerometerData.acceleration.x >= treshold {
 | 
			
		||||
            return .landscapeLeft
 | 
			
		||||
        } else if accelerometerData.acceleration.x <= -treshold {
 | 
			
		||||
            return .landscapeRight
 | 
			
		||||
        } else if accelerometerData.acceleration.y <= -treshold {
 | 
			
		||||
            return .portrait
 | 
			
		||||
        } else if accelerometerData.acceleration.y >= treshold {
 | 
			
		||||
            return .portraitUpsideDown
 | 
			
		||||
        } else {
 | 
			
		||||
            return currentDeviceOrientation
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user