mirror of
https://github.com/yattee/yattee.git
synced 2024-12-23 14:03:41 +00:00
Orientation improvements
This commit is contained in:
parent
868e5fcbc7
commit
d93e9294db
@ -92,7 +92,6 @@ extension Defaults.Keys {
|
|||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
static let honorSystemOrientationLock = Key<Bool>("honorSystemOrientationLock", default: true)
|
static let honorSystemOrientationLock = Key<Bool>("honorSystemOrientationLock", default: true)
|
||||||
static let enterFullscreenInLandscape = Key<Bool>("enterFullscreenInLandscape", default: UIDevice.current.userInterfaceIdiom == .phone)
|
static let enterFullscreenInLandscape = Key<Bool>("enterFullscreenInLandscape", default: UIDevice.current.userInterfaceIdiom == .phone)
|
||||||
static let lockOrientationInFullScreen = Key<Bool>("lockOrientationInFullScreen", default: false)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static let showMPVPlaybackStats = Key<Bool>("showMPVPlaybackStats", default: false)
|
static let showMPVPlaybackStats = Key<Bool>("showMPVPlaybackStats", default: false)
|
||||||
|
@ -136,40 +136,13 @@ extension AppleAVPlayerViewController: AVPlayerViewControllerDelegate {
|
|||||||
|
|
||||||
func playerViewController(
|
func playerViewController(
|
||||||
_: AVPlayerViewController,
|
_: AVPlayerViewController,
|
||||||
willBeginFullScreenPresentationWithAnimationCoordinator context: UIViewControllerTransitionCoordinator
|
willBeginFullScreenPresentationWithAnimationCoordinator _: UIViewControllerTransitionCoordinator
|
||||||
) {
|
) {}
|
||||||
#if os(iOS)
|
|
||||||
if !context.isCancelled, Defaults[.lockOrientationInFullScreen] {
|
|
||||||
Orientation.lockOrientation(.landscape, andRotateTo: UIDevice.current.orientation.isLandscape ? nil : .landscapeRight)
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
func playerViewController(
|
func playerViewController(
|
||||||
_: AVPlayerViewController,
|
_: AVPlayerViewController,
|
||||||
willEndFullScreenPresentationWithAnimationCoordinator coordinator: UIViewControllerTransitionCoordinator
|
willEndFullScreenPresentationWithAnimationCoordinator _: 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func playerViewController(
|
func playerViewController(
|
||||||
_: AVPlayerViewController,
|
_: AVPlayerViewController,
|
||||||
|
@ -38,7 +38,6 @@ struct VideoPlayerView: View {
|
|||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@Environment(\.verticalSizeClass) private var verticalSizeClass
|
@Environment(\.verticalSizeClass) private var verticalSizeClass
|
||||||
|
|
||||||
@State private var motionManager: CMMotionManager!
|
|
||||||
@State private var orientation = UIInterfaceOrientation.portrait
|
@State private var orientation = UIInterfaceOrientation.portrait
|
||||||
@State private var lastOrientation: UIInterfaceOrientation?
|
@State private var lastOrientation: UIInterfaceOrientation?
|
||||||
#elseif os(macOS)
|
#elseif os(macOS)
|
||||||
@ -94,7 +93,7 @@ struct VideoPlayerView: View {
|
|||||||
playerSize = geometry.size
|
playerSize = geometry.size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// .ignoresSafeArea(.all, edges: playerEdgesIgnoringSafeArea)
|
.ignoresSafeArea(.all, edges: playerEdgesIgnoringSafeArea)
|
||||||
.onChange(of: geometry.size) { size in
|
.onChange(of: geometry.size) { size in
|
||||||
self.playerSize = size
|
self.playerSize = size
|
||||||
}
|
}
|
||||||
@ -102,9 +101,6 @@ struct VideoPlayerView: View {
|
|||||||
player.backend.setNeedsDrawing(!value)
|
player.backend.setNeedsDrawing(!value)
|
||||||
}
|
}
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
.onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
|
|
||||||
handleOrientationDidChangeNotification()
|
|
||||||
}
|
|
||||||
.onChange(of: player.presentingPlayer) { newValue in
|
.onChange(of: player.presentingPlayer) { newValue in
|
||||||
if newValue {
|
if newValue {
|
||||||
viewVerticalOffset = 0
|
viewVerticalOffset = 0
|
||||||
@ -120,9 +116,6 @@ struct VideoPlayerView: View {
|
|||||||
} else {
|
} else {
|
||||||
Orientation.lockOrientation(.allButUpsideDown)
|
Orientation.lockOrientation(.allButUpsideDown)
|
||||||
}
|
}
|
||||||
|
|
||||||
motionManager?.stopAccelerometerUpdates()
|
|
||||||
motionManager = nil
|
|
||||||
viewVerticalOffset = Self.hiddenOffset
|
viewVerticalOffset = Self.hiddenOffset
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -203,7 +196,6 @@ struct VideoPlayerView: View {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// .ignoresSafeArea(.all, edges: fullScreenLayout ? .bottom : Edge.Set())
|
|
||||||
.frame(maxWidth: fullScreenLayout ? .infinity : nil, maxHeight: fullScreenLayout ? .infinity : nil)
|
.frame(maxWidth: fullScreenLayout ? .infinity : nil, maxHeight: fullScreenLayout ? .infinity : nil)
|
||||||
.onHover { hovering in
|
.onHover { hovering in
|
||||||
hoveringPlayer = hovering
|
hoveringPlayer = hovering
|
||||||
@ -253,7 +245,7 @@ struct VideoPlayerView: View {
|
|||||||
.background(Color.black)
|
.background(Color.black)
|
||||||
|
|
||||||
#if !os(tvOS)
|
#if !os(tvOS)
|
||||||
if !player.playingFullScreen {
|
if !fullScreenLayout {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
VideoDetails(sidebarQueue: sidebarQueue, fullScreen: $fullScreenDetails)
|
VideoDetails(sidebarQueue: sidebarQueue, fullScreen: $fullScreenDetails)
|
||||||
@ -281,7 +273,7 @@ struct VideoPlayerView: View {
|
|||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
.frame(minWidth: 650)
|
.frame(minWidth: 650)
|
||||||
#endif
|
#endif
|
||||||
if !player.playingFullScreen {
|
if !fullScreenLayout {
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
if sidebarQueue {
|
if sidebarQueue {
|
||||||
PlayerQueueView(sidebarQueue: true, fullScreen: $fullScreenDetails)
|
PlayerQueueView(sidebarQueue: true, fullScreen: $fullScreenDetails)
|
||||||
@ -297,7 +289,7 @@ struct VideoPlayerView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
.statusBar(hidden: player.playingFullScreen)
|
.statusBar(hidden: fullScreenLayout)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -339,18 +331,26 @@ struct VideoPlayerView: View {
|
|||||||
PlayerGestures()
|
PlayerGestures()
|
||||||
PlayerControls(player: player, thumbnails: thumbnails)
|
PlayerControls(player: player, thumbnails: thumbnails)
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
.padding(.top, fullScreenLayout ? (safeAreaInsets.top.isZero ? safeAreaInsets.bottom : safeAreaInsets.top) : 0)
|
.padding(.top, controlsTopPadding)
|
||||||
.padding(.bottom, fullScreenLayout ? safeAreaInsets.bottom : 0)
|
.padding(.bottom, fullScreenLayout ? safeAreaInsets.bottom : 0)
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
.ignoresSafeArea(.all, edges: fullScreenLayout ? .vertical : Edge.Set())
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
.statusBarHidden(fullScreenLayout)
|
.statusBarHidden(fullScreenLayout)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#if os(iOS)
|
#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 {
|
var safeAreaInsets: UIEdgeInsets {
|
||||||
UIApplication.shared.windows.first?.safeAreaInsets ?? .init()
|
UIApplication.shared.windows.first?.safeAreaInsets ?? .init()
|
||||||
}
|
}
|
||||||
@ -432,7 +432,7 @@ struct VideoPlayerView: View {
|
|||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
private func configureOrientationUpdatesBasedOnAccelerometer() {
|
private func configureOrientationUpdatesBasedOnAccelerometer() {
|
||||||
if UIDevice.current.orientation.isLandscape,
|
if OrientationTracker.shared.currentInterfaceOrientation.isLandscape,
|
||||||
Defaults[.enterFullscreenInLandscape],
|
Defaults[.enterFullscreenInLandscape],
|
||||||
!player.playingFullScreen,
|
!player.playingFullScreen,
|
||||||
!player.playingInPictureInPicture
|
!player.playingInPictureInPicture
|
||||||
@ -442,32 +442,16 @@ struct VideoPlayerView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
guard !Defaults[.honorSystemOrientationLock], motionManager.isNil else {
|
NotificationCenter.default.addObserver(
|
||||||
return
|
forName: OrientationTracker.deviceOrientationChangedNotification,
|
||||||
}
|
object: nil,
|
||||||
|
queue: .main
|
||||||
motionManager = CMMotionManager()
|
) { _ in
|
||||||
motionManager.accelerometerUpdateInterval = 0.2
|
guard !Defaults[.honorSystemOrientationLock], player.presentingPlayer, !player.playingInPictureInPicture else {
|
||||||
motionManager.startAccelerometerUpdates(to: OperationQueue()) { data, _ in
|
|
||||||
guard player.presentingPlayer, !player.playingInPictureInPicture, !data.isNil else {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let acceleration = data?.acceleration else {
|
let orientation = OrientationTracker.shared.currentInterfaceOrientation
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
guard lastOrientation != orientation else {
|
guard lastOrientation != orientation else {
|
||||||
return
|
return
|
||||||
@ -475,67 +459,21 @@ struct VideoPlayerView: View {
|
|||||||
|
|
||||||
lastOrientation = orientation
|
lastOrientation = orientation
|
||||||
|
|
||||||
if orientation.isLandscape {
|
DispatchQueue.main.async {
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.8) {
|
guard Defaults[.enterFullscreenInLandscape] else {
|
||||||
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 {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
Orientation.lockOrientation(.portrait)
|
if orientation.isLandscape {
|
||||||
}
|
player.enterFullScreen()
|
||||||
}
|
Orientation.lockOrientation(OrientationTracker.shared.currentInterfaceOrientationMask, andRotateTo: orientation)
|
||||||
}
|
} else {
|
||||||
|
if !player.playingFullScreen {
|
||||||
private func handleOrientationDidChangeNotification() {
|
player.exitFullScreen()
|
||||||
viewVerticalOffset = viewVerticalOffset == 0 ? 0 : Self.hiddenOffset
|
} else {
|
||||||
let newOrientation = UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
|
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: .portrait)
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@ struct PlayerSettings: View {
|
|||||||
@Default(.closeLastItemOnPlaybackEnd) private var closeLastItemOnPlaybackEnd
|
@Default(.closeLastItemOnPlaybackEnd) private var closeLastItemOnPlaybackEnd
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@Default(.honorSystemOrientationLock) private var honorSystemOrientationLock
|
@Default(.honorSystemOrientationLock) private var honorSystemOrientationLock
|
||||||
@Default(.lockOrientationInFullScreen) private var lockOrientationInFullScreen
|
|
||||||
@Default(.enterFullscreenInLandscape) private var enterFullscreenInLandscape
|
@Default(.enterFullscreenInLandscape) private var enterFullscreenInLandscape
|
||||||
#endif
|
#endif
|
||||||
@Default(.closePiPOnNavigation) private var closePiPOnNavigation
|
@Default(.closePiPOnNavigation) private var closePiPOnNavigation
|
||||||
@ -93,7 +92,6 @@ struct PlayerSettings: View {
|
|||||||
enterFullscreenInLandscapeToggle
|
enterFullscreenInLandscapeToggle
|
||||||
}
|
}
|
||||||
honorSystemOrientationLockToggle
|
honorSystemOrientationLockToggle
|
||||||
lockOrientationInFullScreenToggle
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -186,11 +184,6 @@ struct PlayerSettings: View {
|
|||||||
private var enterFullscreenInLandscapeToggle: some View {
|
private var enterFullscreenInLandscapeToggle: some View {
|
||||||
Toggle("Enter fullscreen in landscape", isOn: $enterFullscreenInLandscape)
|
Toggle("Enter fullscreen in landscape", isOn: $enterFullscreenInLandscape)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var lockOrientationInFullScreenToggle: some View {
|
|
||||||
Toggle("Lock orientation in fullscreen", isOn: $lockOrientationInFullScreen)
|
|
||||||
.disabled(!enterFullscreenInLandscape)
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private var closePiPOnNavigationToggle: some View {
|
private var closePiPOnNavigationToggle: some View {
|
||||||
|
@ -511,6 +511,7 @@
|
|||||||
379775932689365600DD52A8 /* Array+Next.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379775922689365600DD52A8 /* Array+Next.swift */; };
|
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 */; };
|
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 */; };
|
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 */; };
|
37A3B15A27255E7F000FB5EE /* SafariWebExtensionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A3B15927255E7F000FB5EE /* SafariWebExtensionHandler.swift */; };
|
||||||
37A3B15F27255E7F000FB5EE /* images in Resources */ = {isa = PBXBuildFile; fileRef = 37A3B15E27255E7F000FB5EE /* images */; };
|
37A3B15F27255E7F000FB5EE /* images in Resources */ = {isa = PBXBuildFile; fileRef = 37A3B15E27255E7F000FB5EE /* images */; };
|
||||||
37A3B16127255E7F000FB5EE /* manifest.json in Resources */ = {isa = PBXBuildFile; fileRef = 37A3B16027255E7F000FB5EE /* manifest.json */; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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; };
|
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>"; };
|
37A3B15927255E7F000FB5EE /* SafariWebExtensionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SafariWebExtensionHandler.swift; sourceTree = "<group>"; };
|
||||||
37A3B15E27255E7F000FB5EE /* images */ = {isa = PBXFileReference; lastKnownFileType = folder; path = images; sourceTree = "<group>"; };
|
37A3B15E27255E7F000FB5EE /* images */ = {isa = PBXFileReference; lastKnownFileType = folder; path = images; sourceTree = "<group>"; };
|
||||||
@ -1775,6 +1777,7 @@
|
|||||||
children = (
|
children = (
|
||||||
37B4E802277D0A72004BF56A /* AppDelegate.swift */,
|
37B4E802277D0A72004BF56A /* AppDelegate.swift */,
|
||||||
37B4E804277D0AB4004BF56A /* Orientation.swift */,
|
37B4E804277D0AB4004BF56A /* Orientation.swift */,
|
||||||
|
379B0252287A1CDF001015B5 /* OrientationTracker.swift */,
|
||||||
3784B23A272894DA00B09468 /* ShareSheet.swift */,
|
3784B23A272894DA00B09468 /* ShareSheet.swift */,
|
||||||
3749BF9227ADA142000480FF /* BridgingHeader.h */,
|
3749BF9227ADA142000480FF /* BridgingHeader.h */,
|
||||||
37992DC726CC50BC003D4C27 /* Info.plist */,
|
37992DC726CC50BC003D4C27 /* Info.plist */,
|
||||||
@ -2832,6 +2835,7 @@
|
|||||||
37E70923271CD43000D34DDE /* WelcomeScreen.swift in Sources */,
|
37E70923271CD43000D34DDE /* WelcomeScreen.swift in Sources */,
|
||||||
37BD07BB2698AB60003EBB87 /* AppSidebarNavigation.swift in Sources */,
|
37BD07BB2698AB60003EBB87 /* AppSidebarNavigation.swift in Sources */,
|
||||||
37C0697A2725C09E00F7F6CB /* PlayerQueueItemBridge.swift in Sources */,
|
37C0697A2725C09E00F7F6CB /* PlayerQueueItemBridge.swift in Sources */,
|
||||||
|
379B0253287A1CDF001015B5 /* OrientationTracker.swift in Sources */,
|
||||||
370B79C9286279810045DB77 /* NSObject+Swizzle.swift in Sources */,
|
370B79C9286279810045DB77 /* NSObject+Swizzle.swift in Sources */,
|
||||||
37D4B0E42671614900C925CA /* YatteeApp.swift in Sources */,
|
37D4B0E42671614900C925CA /* YatteeApp.swift in Sources */,
|
||||||
37C3A241272359900087A57A /* Double+Format.swift in Sources */,
|
37C3A241272359900087A57A /* Double+Format.swift in Sources */,
|
||||||
|
@ -19,6 +19,8 @@ final class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||||||
UITabBar.appearance().backgroundImage = UIImage()
|
UITabBar.appearance().backgroundImage = UIImage()
|
||||||
UITabBar.appearance().isTranslucent = true
|
UITabBar.appearance().isTranslucent = true
|
||||||
UITabBar.appearance().backgroundColor = .clear
|
UITabBar.appearance().backgroundColor = .clear
|
||||||
|
|
||||||
|
OrientationTracker.shared.startDeviceOrientationTracking()
|
||||||
#endif
|
#endif
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -21,11 +21,16 @@ struct Orientation {
|
|||||||
static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation: UIInterfaceOrientation? = nil) {
|
static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation: UIInterfaceOrientation? = nil) {
|
||||||
lockOrientation(orientation)
|
lockOrientation(orientation)
|
||||||
|
|
||||||
guard !rotateOrientation.isNil else {
|
guard let rotateOrientation = rotateOrientation else {
|
||||||
return
|
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()
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user