mirror of
https://github.com/yattee/yattee.git
synced 2025-08-09 04:04:07 +00:00
Add iOS options for handling landscape fullscreen (fixes #38)
This commit is contained in:
@@ -82,6 +82,12 @@ extension Defaults.Keys {
|
||||
#if os(macOS)
|
||||
static let enableBetaChannel = Key<Bool>("enableBetaChannel", default: false)
|
||||
#endif
|
||||
|
||||
#if os(iOS)
|
||||
static let honorSystemOrientationLock = Key<Bool>("honorSystemOrientationLock", default: true)
|
||||
static let enterFullscreenInLandscape = Key<Bool>("enterFullscreenInLandscape", default: UIDevice.current.userInterfaceIdiom == .phone)
|
||||
static let lockLandscapeWhenEnteringFullscreen = Key<Bool>("lockLandscapeWhenEnteringFullscreen", default: false)
|
||||
#endif
|
||||
}
|
||||
|
||||
enum ResolutionSetting: String, CaseIterable, Defaults.Serializable {
|
||||
|
@@ -132,17 +132,32 @@ extension PlayerViewController: AVPlayerViewControllerDelegate {
|
||||
func playerViewController(
|
||||
_: AVPlayerViewController,
|
||||
willBeginFullScreenPresentationWithAnimationCoordinator _: UIViewControllerTransitionCoordinator
|
||||
) {}
|
||||
) {
|
||||
playerModel.playingFullscreen = true
|
||||
}
|
||||
|
||||
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)
|
||||
if self.traitCollection.verticalSizeClass == .compact {
|
||||
self.dismiss(animated: true)
|
||||
self.playerModel.lockedOrientation = nil
|
||||
if Defaults[.enterFullscreenInLandscape] {
|
||||
Orientation.lockOrientation(.portrait, andRotateTo: .portrait)
|
||||
}
|
||||
|
||||
self.playerModel.playingFullscreen = false
|
||||
|
||||
if wasPlaying {
|
||||
self.playerModel.play()
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@@ -1,4 +1,7 @@
|
||||
import AVKit
|
||||
#if os(iOS)
|
||||
import CoreMotion
|
||||
#endif
|
||||
import Defaults
|
||||
import Siesta
|
||||
import SwiftUI
|
||||
@@ -22,6 +25,16 @@ struct VideoPlayerView: View {
|
||||
@Environment(\.presentationMode) private var presentationMode
|
||||
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
|
||||
@Environment(\.verticalSizeClass) private var verticalSizeClass
|
||||
|
||||
@Default(.enterFullscreenInLandscape) private var enterFullscreenInLandscape
|
||||
@Default(.honorSystemOrientationLock) private var honorSystemOrientationLock
|
||||
@Default(.lockLandscapeWhenEnteringFullscreen) private var lockLandscapeWhenEnteringFullscreen
|
||||
|
||||
@State private var motionManager: CMMotionManager!
|
||||
@State private var orientation = UIInterfaceOrientation.portrait
|
||||
@State private var lastOrientation: UIInterfaceOrientation?
|
||||
|
||||
private var orientationThrottle = Throttle(interval: 2)
|
||||
#endif
|
||||
|
||||
@EnvironmentObject<AccountsModel> private var accounts
|
||||
@@ -38,13 +51,36 @@ struct VideoPlayerView: View {
|
||||
GeometryReader { geometry in
|
||||
HStack(spacing: 0) {
|
||||
content
|
||||
}
|
||||
.onAppear {
|
||||
self.playerSize = geometry.size
|
||||
.onAppear {
|
||||
playerSize = geometry.size
|
||||
|
||||
#if os(iOS)
|
||||
configureOrientationUpdatesBasedOnAccelerometer()
|
||||
#endif
|
||||
}
|
||||
}
|
||||
.onChange(of: geometry.size) { size in
|
||||
self.playerSize = size
|
||||
}
|
||||
#if os(iOS)
|
||||
.onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
|
||||
handleOrientationDidChangeNotification()
|
||||
}
|
||||
.onDisappear {
|
||||
guard !player.playingFullscreen else {
|
||||
return // swiftlint:disable:this implicit_return
|
||||
}
|
||||
|
||||
if Defaults[.lockPortraitWhenBrowsing] {
|
||||
Orientation.lockOrientation(.portrait, andRotateTo: .portrait)
|
||||
} else {
|
||||
Orientation.lockOrientation(.allButUpsideDown)
|
||||
}
|
||||
|
||||
motionManager?.stopAccelerometerUpdates()
|
||||
motionManager = nil
|
||||
}
|
||||
#endif
|
||||
}
|
||||
.navigationBarHidden(true)
|
||||
#endif
|
||||
@@ -192,6 +228,110 @@ struct VideoPlayerView: View {
|
||||
set: { _ in }
|
||||
)
|
||||
}
|
||||
|
||||
#if os(iOS)
|
||||
private func configureOrientationUpdatesBasedOnAccelerometer() {
|
||||
if UIDevice.current.orientation.isLandscape, enterFullscreenInLandscape, !player.playingFullscreen {
|
||||
DispatchQueue.main.async {
|
||||
player.enterFullScreen()
|
||||
}
|
||||
}
|
||||
|
||||
guard !honorSystemOrientationLock, motionManager.isNil else {
|
||||
return
|
||||
}
|
||||
|
||||
motionManager = CMMotionManager()
|
||||
motionManager.accelerometerUpdateInterval = 0.2
|
||||
motionManager.startAccelerometerUpdates(to: OperationQueue()) { data, _ in
|
||||
guard player.presentingPlayer, !data.isNil 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
|
||||
}
|
||||
|
||||
guard lastOrientation != orientation else {
|
||||
return
|
||||
}
|
||||
|
||||
lastOrientation = orientation
|
||||
|
||||
if orientation.isLandscape {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.8) {
|
||||
guard enterFullscreenInLandscape else {
|
||||
return
|
||||
}
|
||||
|
||||
player.enterFullScreen()
|
||||
|
||||
let orientationLockMask = orientation == .landscapeLeft ? UIInterfaceOrientationMask.landscapeLeft : .landscapeRight
|
||||
|
||||
Orientation.lockOrientation(orientationLockMask, andRotateTo: orientation)
|
||||
|
||||
guard lockLandscapeWhenEnteringFullscreen else {
|
||||
return
|
||||
}
|
||||
|
||||
player.lockedOrientation = orientation
|
||||
}
|
||||
} else {
|
||||
guard abs(acceleration.z) <= 0.74,
|
||||
player.lockedOrientation.isNil,
|
||||
enterFullscreenInLandscape
|
||||
else {
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
|
||||
player.exitFullScreen()
|
||||
}
|
||||
|
||||
Orientation.lockOrientation(.portrait)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func handleOrientationDidChangeNotification() {
|
||||
let newOrientation = UIApplication.shared.windows.first?.windowScene?.interfaceOrientation
|
||||
if newOrientation?.isLandscape ?? false, player.presentingPlayer, lockLandscapeWhenEnteringFullscreen, !player.lockedOrientation.isNil {
|
||||
Orientation.lockOrientation(.landscape, andRotateTo: newOrientation)
|
||||
return
|
||||
}
|
||||
|
||||
guard player.presentingPlayer, enterFullscreenInLandscape, 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
struct VideoPlayerView_Previews: PreviewProvider {
|
||||
|
@@ -10,6 +10,11 @@ struct PlaybackSettings: View {
|
||||
@Default(.showKeywords) private var showKeywords
|
||||
@Default(.showChannelSubscribers) private var channelSubscribers
|
||||
@Default(.pauseOnHidingPlayer) private var pauseOnHidingPlayer
|
||||
#if os(iOS)
|
||||
@Default(.honorSystemOrientationLock) private var honorSystemOrientationLock
|
||||
@Default(.lockLandscapeWhenEnteringFullscreen) private var lockLandscapeWhenEnteringFullscreen
|
||||
@Default(.enterFullscreenInLandscape) private var enterFullscreenInLandscape
|
||||
#endif
|
||||
@Default(.closePiPOnNavigation) private var closePiPOnNavigation
|
||||
@Default(.closePiPOnOpeningPlayer) private var closePiPOnOpeningPlayer
|
||||
#if !os(macOS)
|
||||
@@ -37,6 +42,13 @@ struct PlaybackSettings: View {
|
||||
showHistoryToggle
|
||||
channelSubscribersToggle
|
||||
pauseOnHidingPlayerToggle
|
||||
|
||||
if idiom == .pad {
|
||||
enterFullscreenInLandscapeToggle
|
||||
}
|
||||
|
||||
honorSystemOrientationLockToggle
|
||||
lockLandscapeWhenEnteringFullscreenToggle
|
||||
}
|
||||
|
||||
Section(header: SettingsHeader(text: "Picture in Picture")) {
|
||||
@@ -147,6 +159,22 @@ struct PlaybackSettings: View {
|
||||
Toggle("Pause when player is closed", isOn: $pauseOnHidingPlayer)
|
||||
}
|
||||
|
||||
#if os(iOS)
|
||||
private var honorSystemOrientationLockToggle: some View {
|
||||
Toggle("Honor system orientation lock", isOn: $honorSystemOrientationLock)
|
||||
.disabled(!enterFullscreenInLandscape)
|
||||
}
|
||||
|
||||
private var enterFullscreenInLandscapeToggle: some View {
|
||||
Toggle("Enter fullscreen in landscape", isOn: $enterFullscreenInLandscape)
|
||||
}
|
||||
|
||||
private var lockLandscapeWhenEnteringFullscreenToggle: some View {
|
||||
Toggle("Lock landscape orientation when entering fullscreen", isOn: $lockLandscapeWhenEnteringFullscreen)
|
||||
.disabled(!enterFullscreenInLandscape)
|
||||
}
|
||||
#endif
|
||||
|
||||
private var closePiPOnNavigationToggle: some View {
|
||||
Toggle("Close PiP when starting playing other video", isOn: $closePiPOnNavigation)
|
||||
}
|
||||
|
Reference in New Issue
Block a user