Add iOS options for handling landscape fullscreen (fixes #38)

This commit is contained in:
Arkadiusz Fal
2022-01-02 20:43:30 +01:00
parent d6e75295e1
commit 00778b585f
5 changed files with 234 additions and 14 deletions

View File

@@ -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
}

View File

@@ -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 {