mirror of
https://github.com/yattee/yattee.git
synced 2024-12-22 21:43:41 +00:00
Improve player transitions
This commit is contained in:
parent
fb40f42c6c
commit
bcc1d5aeaa
@ -51,7 +51,7 @@ struct FixtureEnvironmentObjectsModifier: ViewModifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var playerControls: PlayerControlsModel {
|
private var playerControls: PlayerControlsModel {
|
||||||
PlayerControlsModel(presentingControls: true, presentingControlsOverlay: true, player: player)
|
PlayerControlsModel(presentingControls: true, presentingControlsOverlay: false, player: player)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var subscriptions: SubscriptionsModel {
|
private var subscriptions: SubscriptionsModel {
|
||||||
|
@ -40,7 +40,7 @@ final class PlayerModel: ObservableObject {
|
|||||||
|
|
||||||
var mpvPlayerView = MPVPlayerView()
|
var mpvPlayerView = MPVPlayerView()
|
||||||
|
|
||||||
@Published var presentingPlayer = false { didSet { handlePresentationChange() } }
|
@Published var presentingPlayer = true { didSet { handlePresentationChange() } }
|
||||||
@Published var activeBackend = PlayerBackendType.mpv
|
@Published var activeBackend = PlayerBackendType.mpv
|
||||||
|
|
||||||
var avPlayerBackend: AVPlayerBackend!
|
var avPlayerBackend: AVPlayerBackend!
|
||||||
@ -149,6 +149,7 @@ final class PlayerModel: ObservableObject {
|
|||||||
@Default(.pauseOnHidingPlayer) private var pauseOnHidingPlayer
|
@Default(.pauseOnHidingPlayer) private var pauseOnHidingPlayer
|
||||||
@Default(.closePiPOnNavigation) var closePiPOnNavigation
|
@Default(.closePiPOnNavigation) var closePiPOnNavigation
|
||||||
@Default(.closePiPOnOpeningPlayer) var closePiPOnOpeningPlayer
|
@Default(.closePiPOnOpeningPlayer) var closePiPOnOpeningPlayer
|
||||||
|
@Default(.resetWatchedStatusOnPlaying) var resetWatchedStatusOnPlaying
|
||||||
|
|
||||||
#if !os(macOS)
|
#if !os(macOS)
|
||||||
@Default(.closePiPAndOpenPlayerOnEnteringForeground) var closePiPAndOpenPlayerOnEnteringForeground
|
@Default(.closePiPAndOpenPlayerOnEnteringForeground) var closePiPAndOpenPlayerOnEnteringForeground
|
||||||
@ -202,7 +203,7 @@ final class PlayerModel: ObservableObject {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
DispatchQueue.main.async { [weak self] in
|
DispatchQueue.main.async { [weak self] in
|
||||||
withAnimation {
|
withAnimation(.linear(duration: 0.25)) {
|
||||||
self?.presentingPlayer = true
|
self?.presentingPlayer = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -214,11 +215,12 @@ final class PlayerModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func hide() {
|
func hide() {
|
||||||
|
withAnimation(.linear(duration: 0.25)) {
|
||||||
|
presentingPlayer = false
|
||||||
|
}
|
||||||
|
|
||||||
DispatchQueue.main.async { [weak self] in
|
DispatchQueue.main.async { [weak self] in
|
||||||
self?.playingFullScreen = false
|
self?.playingFullScreen = false
|
||||||
withAnimation {
|
|
||||||
self?.presentingPlayer = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@ -742,22 +744,22 @@ final class PlayerModel: ObservableObject {
|
|||||||
pause()
|
pause()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func enterFullScreen(showControls: Bool = true) {
|
|
||||||
guard !playingFullScreen else { return }
|
|
||||||
|
|
||||||
logger.info("entering fullscreen")
|
|
||||||
toggleFullscreen(false, showControls: showControls)
|
|
||||||
}
|
|
||||||
|
|
||||||
func exitFullScreen(showControls: Bool = true) {
|
|
||||||
guard playingFullScreen else { return }
|
|
||||||
|
|
||||||
logger.info("exiting fullscreen")
|
|
||||||
toggleFullscreen(true, showControls: showControls)
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
func enterFullScreen(showControls: Bool = true) {
|
||||||
|
guard !playingFullScreen else { return }
|
||||||
|
|
||||||
|
logger.info("entering fullscreen")
|
||||||
|
toggleFullscreen(false, showControls: showControls)
|
||||||
|
}
|
||||||
|
|
||||||
|
func exitFullScreen(showControls: Bool = true) {
|
||||||
|
guard playingFullScreen else { return }
|
||||||
|
|
||||||
|
logger.info("exiting fullscreen")
|
||||||
|
toggleFullscreen(true, showControls: showControls)
|
||||||
|
}
|
||||||
|
|
||||||
func updateNowPlayingInfo() {
|
func updateNowPlayingInfo() {
|
||||||
guard let video = currentItem?.video else {
|
guard let video = currentItem?.video else {
|
||||||
return
|
return
|
||||||
@ -818,25 +820,10 @@ final class PlayerModel: ObservableObject {
|
|||||||
controls.presentingControls = showControls && isFullScreen
|
controls.presentingControls = showControls && isFullScreen
|
||||||
|
|
||||||
#if os(macOS)
|
#if os(macOS)
|
||||||
if isFullScreen {
|
Windows.player.toggleFullScreen()
|
||||||
Windows.player.toggleFullScreen()
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#if os(iOS)
|
|
||||||
withAnimation(.linear(duration: 0.2)) {
|
|
||||||
playingFullScreen = !isFullScreen
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
playingFullScreen = !isFullScreen
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if os(macOS)
|
playingFullScreen = !isFullScreen
|
||||||
if !isFullScreen {
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) {
|
|
||||||
Windows.player.toggleFullScreen()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
if !playingFullScreen {
|
if !playingFullScreen {
|
||||||
|
@ -90,6 +90,7 @@ extension Defaults.Keys {
|
|||||||
static let trendingCountry = Key<Country>("trendingCountry", default: .us)
|
static let trendingCountry = Key<Country>("trendingCountry", default: .us)
|
||||||
|
|
||||||
static let visibleSections = Key<Set<VisibleSection>>("visibleSections", default: [.favorites, .subscriptions, .trending, .playlists])
|
static let visibleSections = Key<Set<VisibleSection>>("visibleSections", default: [.favorites, .subscriptions, .trending, .playlists])
|
||||||
|
static let videoDetailsPage = Key<VideoDetails.DetailsPage>("videoDetailsPage", default: .info)
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
static let honorSystemOrientationLock = Key<Bool>("honorSystemOrientationLock", default: true)
|
static let honorSystemOrientationLock = Key<Bool>("honorSystemOrientationLock", default: true)
|
||||||
|
@ -131,18 +131,21 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder var videoPlayer: some View {
|
@ViewBuilder var videoPlayer: some View {
|
||||||
VideoPlayerView()
|
if player.presentingPlayer {
|
||||||
.environmentObject(accounts)
|
VideoPlayerView()
|
||||||
.environmentObject(comments)
|
.environmentObject(accounts)
|
||||||
.environmentObject(instances)
|
.environmentObject(comments)
|
||||||
.environmentObject(navigation)
|
.environmentObject(instances)
|
||||||
.environmentObject(player)
|
.environmentObject(navigation)
|
||||||
.environmentObject(playerControls)
|
.environmentObject(player)
|
||||||
.environmentObject(playlists)
|
.environmentObject(playerControls)
|
||||||
.environmentObject(recents)
|
.environmentObject(playlists)
|
||||||
.environmentObject(subscriptions)
|
.environmentObject(recents)
|
||||||
.environmentObject(thumbnailsModel)
|
.environmentObject(subscriptions)
|
||||||
.environment(\.navigationStyle, navigationStyle)
|
.environmentObject(thumbnailsModel)
|
||||||
|
.environment(\.navigationStyle, navigationStyle)
|
||||||
|
.transition(.move(edge: .bottom))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import SwiftUI
|
|||||||
import SwiftUIPager
|
import SwiftUIPager
|
||||||
|
|
||||||
struct VideoDetails: View {
|
struct VideoDetails: View {
|
||||||
enum DetailsPage: CaseIterable {
|
enum DetailsPage: String, CaseIterable, Defaults.Serializable {
|
||||||
case info, chapters, comments, related, queue
|
case info, chapters, comments, related, queue
|
||||||
|
|
||||||
var index: Int {
|
var index: Int {
|
||||||
@ -41,6 +41,7 @@ struct VideoDetails: View {
|
|||||||
@EnvironmentObject<RecentsModel> private var recents
|
@EnvironmentObject<RecentsModel> private var recents
|
||||||
@EnvironmentObject<SubscriptionsModel> private var subscriptions
|
@EnvironmentObject<SubscriptionsModel> private var subscriptions
|
||||||
|
|
||||||
|
@Default(.videoDetailsPage) private var videoDetailsPage
|
||||||
@Default(.showKeywords) private var showKeywords
|
@Default(.showKeywords) private var showKeywords
|
||||||
@Default(.playerDetailsPageButtonLabelStyle) private var playerDetailsPageButtonLabelStyle
|
@Default(.playerDetailsPageButtonLabelStyle) private var playerDetailsPageButtonLabelStyle
|
||||||
|
|
||||||
@ -53,7 +54,11 @@ struct VideoDetails: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading, spacing: 0) {
|
if #available(iOS 15, macOS 12, *) {
|
||||||
|
Self._printChanges()
|
||||||
|
}
|
||||||
|
|
||||||
|
return VStack(alignment: .leading, spacing: 0) {
|
||||||
ControlsBar(
|
ControlsBar(
|
||||||
fullScreen: $fullScreen,
|
fullScreen: $fullScreen,
|
||||||
presentingControls: false,
|
presentingControls: false,
|
||||||
@ -87,12 +92,12 @@ struct VideoDetails: View {
|
|||||||
if pageIndex == DetailsPage.comments.index {
|
if pageIndex == DetailsPage.comments.index {
|
||||||
comments.load()
|
comments.load()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
videoDetailsPage = DetailsPage.allCases.first { $0.index == pageIndex } ?? .info
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
if video.isNil && !sidebarQueue {
|
page.update(.new(index: videoDetailsPage.index))
|
||||||
page.update(.new(index: DetailsPage.queue.index))
|
|
||||||
}
|
|
||||||
|
|
||||||
guard video != nil, accounts.app.supportsSubscriptions else {
|
guard video != nil, accounts.app.supportsSubscriptions else {
|
||||||
subscribed = false
|
subscribed = false
|
||||||
@ -139,6 +144,7 @@ struct VideoDetails: View {
|
|||||||
Button(action: {
|
Button(action: {
|
||||||
page.update(.new(index: destination.index))
|
page.update(.new(index: destination.index))
|
||||||
pageChangeAction?()
|
pageChangeAction?()
|
||||||
|
videoDetailsPage = destination
|
||||||
}) {
|
}) {
|
||||||
HStack {
|
HStack {
|
||||||
Spacer()
|
Spacer()
|
||||||
@ -180,13 +186,14 @@ struct VideoDetails: View {
|
|||||||
case .chapters:
|
case .chapters:
|
||||||
ChaptersView()
|
ChaptersView()
|
||||||
|
|
||||||
case .queue:
|
case .comments:
|
||||||
PlayerQueueView(sidebarQueue: sidebarQueue, fullScreen: $fullScreen)
|
CommentsView(embedInScrollView: true)
|
||||||
|
|
||||||
case .related:
|
case .related:
|
||||||
RelatedView()
|
RelatedView()
|
||||||
case .comments:
|
|
||||||
CommentsView(embedInScrollView: true)
|
case .queue:
|
||||||
|
PlayerQueueView(sidebarQueue: sidebarQueue, fullScreen: $fullScreen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.contentShape(Rectangle())
|
.contentShape(Rectangle())
|
||||||
|
@ -9,6 +9,9 @@ import SwiftUI
|
|||||||
struct VideoPlayerView: View {
|
struct VideoPlayerView: View {
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
static let hiddenOffset = YatteeApp.isForPreviews ? 0 : max(UIScreen.main.bounds.height, UIScreen.main.bounds.width) + 100
|
static let hiddenOffset = YatteeApp.isForPreviews ? 0 : max(UIScreen.main.bounds.height, UIScreen.main.bounds.width) + 100
|
||||||
|
static let defaultSidebarQueueValue = UIScreen.main.bounds.width > 900 && Defaults[.playerSidebar] == .whenFits
|
||||||
|
#else
|
||||||
|
static let defaultSidebarQueueValue = Defaults[.playerSidebar] != .never
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static let defaultAspectRatio = 16 / 9.0
|
static let defaultAspectRatio = 16 / 9.0
|
||||||
@ -21,17 +24,11 @@ struct VideoPlayerView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@State private var playerSize: CGSize = .zero { didSet {
|
@State private var playerSize: CGSize = .zero { didSet {
|
||||||
withAnimation {
|
sidebarQueue = playerSize.width > 900 && Defaults[.playerSidebar] == .whenFits
|
||||||
if playerSize.width > 900 && Defaults[.playerSidebar] == .whenFits {
|
|
||||||
sidebarQueue = true
|
|
||||||
} else {
|
|
||||||
sidebarQueue = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
@State private var hoveringPlayer = false
|
@State private var hoveringPlayer = false
|
||||||
@State private var fullScreenDetails = false
|
@State private var fullScreenDetails = false
|
||||||
@State private var sidebarQueue = false
|
@State private var sidebarQueue = defaultSidebarQueueValue
|
||||||
|
|
||||||
@Environment(\.colorScheme) private var colorScheme
|
@Environment(\.colorScheme) private var colorScheme
|
||||||
|
|
||||||
@ -46,7 +43,9 @@ struct VideoPlayerView: View {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
@State private var viewVerticalOffset = Self.hiddenOffset
|
@GestureState private var dragGestureState = false
|
||||||
|
@GestureState private var dragGestureOffset = CGSize.zero
|
||||||
|
@State private var viewDragOffset = 0.0
|
||||||
@State private var orientationObserver: Any?
|
@State private var orientationObserver: Any?
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -58,17 +57,11 @@ struct VideoPlayerView: View {
|
|||||||
@EnvironmentObject<SearchModel> private var search
|
@EnvironmentObject<SearchModel> private var search
|
||||||
@EnvironmentObject<ThumbnailsModel> private var thumbnails
|
@EnvironmentObject<ThumbnailsModel> private var thumbnails
|
||||||
|
|
||||||
init() {
|
|
||||||
if Defaults[.playerSidebar] == .always {
|
|
||||||
sidebarQueue = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
// TODO: remove
|
// TODO: remove
|
||||||
if #available(iOS 15.0, macOS 12.0, *) {
|
if #available(iOS 15.0, macOS 12.0, *) {
|
||||||
_ = Self._printChanges()
|
Self._printChanges()
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -105,10 +98,9 @@ struct VideoPlayerView: View {
|
|||||||
.onChange(of: fullScreenDetails) { value in
|
.onChange(of: fullScreenDetails) { value in
|
||||||
player.backend.setNeedsDrawing(!value)
|
player.backend.setNeedsDrawing(!value)
|
||||||
}
|
}
|
||||||
#if os(iOS)
|
.onAppear {
|
||||||
.onChange(of: player.presentingPlayer) { newValue in
|
#if os(iOS)
|
||||||
if newValue {
|
viewDragOffset = 0.0
|
||||||
viewVerticalOffset = 0
|
|
||||||
configureOrientationUpdatesBasedOnAccelerometer()
|
configureOrientationUpdatesBasedOnAccelerometer()
|
||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak player] in
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak player] in
|
||||||
@ -122,22 +114,25 @@ struct VideoPlayerView: View {
|
|||||||
andRotateTo: orientationMask == .landscapeLeft ? .landscapeLeft : orientationMask == .landscapeRight ? .landscapeRight : .portrait
|
andRotateTo: orientationMask == .landscapeLeft ? .landscapeLeft : orientationMask == .landscapeRight ? .landscapeRight : .portrait
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
#endif
|
||||||
|
}
|
||||||
|
.onDisappear {
|
||||||
|
#if os(iOS)
|
||||||
if Defaults[.lockPortraitWhenBrowsing] {
|
if Defaults[.lockPortraitWhenBrowsing] {
|
||||||
Orientation.lockOrientation(.portrait, andRotateTo: .portrait)
|
Orientation.lockOrientation(.portrait, andRotateTo: .portrait)
|
||||||
} else {
|
} else {
|
||||||
Orientation.lockOrientation(.allButUpsideDown)
|
Orientation.lockOrientation(.allButUpsideDown)
|
||||||
}
|
}
|
||||||
viewVerticalOffset = Self.hiddenOffset
|
|
||||||
stopOrientationUpdates()
|
stopOrientationUpdates()
|
||||||
player.controls.hideOverlays()
|
player.controls.hideOverlays()
|
||||||
}
|
|
||||||
|
player.lockedOrientation = nil
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
.offset(y: viewVerticalOffset)
|
.offset(y: playerOffset)
|
||||||
.animation(.easeOut(duration: 0.3), value: viewVerticalOffset)
|
.animation(.linear(duration: 0.2), value: playerOffset)
|
||||||
.backport
|
.backport
|
||||||
.persistentSystemOverlays(!fullScreenLayout)
|
.persistentSystemOverlays(!fullScreenLayout)
|
||||||
#endif
|
#endif
|
||||||
@ -145,6 +140,10 @@ struct VideoPlayerView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
|
var playerOffset: Double {
|
||||||
|
dragGestureState ? dragGestureOffset.height : viewDragOffset
|
||||||
|
}
|
||||||
|
|
||||||
var playerWidth: Double? {
|
var playerWidth: Double? {
|
||||||
fullScreenLayout ? (UIScreen.main.bounds.size.width - SafeArea.insets.left - SafeArea.insets.right) : nil
|
fullScreenLayout ? (UIScreen.main.bounds.size.width - SafeArea.insets.left - SafeArea.insets.right) : nil
|
||||||
}
|
}
|
||||||
@ -224,6 +223,11 @@ struct VideoPlayerView: View {
|
|||||||
}
|
}
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
.gesture(playerControls.presentingOverlays ? nil : playerDragGesture)
|
.gesture(playerControls.presentingOverlays ? nil : playerDragGesture)
|
||||||
|
.onChange(of: dragGestureState) { _ in
|
||||||
|
if !dragGestureState {
|
||||||
|
onPlayerDragGestureEnded()
|
||||||
|
}
|
||||||
|
}
|
||||||
#elseif os(macOS)
|
#elseif os(macOS)
|
||||||
.onAppear(perform: {
|
.onAppear(perform: {
|
||||||
NSEvent.addLocalMonitorForEvents(matching: [.mouseMoved]) {
|
NSEvent.addLocalMonitorForEvents(matching: [.mouseMoved]) {
|
||||||
@ -242,21 +246,16 @@ struct VideoPlayerView: View {
|
|||||||
|
|
||||||
#if !os(tvOS)
|
#if !os(tvOS)
|
||||||
if !fullScreenLayout {
|
if !fullScreenLayout {
|
||||||
VStack(spacing: 0) {
|
VideoDetails(sidebarQueue: sidebarQueue, fullScreen: $fullScreenDetails)
|
||||||
#if os(iOS)
|
|
||||||
VideoDetails(sidebarQueue: sidebarQueue, fullScreen: $fullScreenDetails)
|
|
||||||
#else
|
|
||||||
VideoDetails(sidebarQueue: sidebarQueue, fullScreen: $fullScreenDetails)
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
.transition(.asymmetric(insertion: .opacity, removal: .identity))
|
.ignoresSafeArea(.all, edges: .bottom)
|
||||||
|
.transition(.move(edge: .bottom))
|
||||||
#endif
|
#endif
|
||||||
.background(colorScheme == .dark ? Color.black : Color.white)
|
.background(colorScheme == .dark ? Color.black : Color.white)
|
||||||
.modifier(VideoDetailsPaddingModifier(
|
.modifier(VideoDetailsPaddingModifier(
|
||||||
playerSize: player.playerSize,
|
playerSize: player.playerSize,
|
||||||
fullScreen: fullScreenDetails
|
fullScreen: fullScreenDetails
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -272,8 +271,8 @@ struct VideoPlayerView: View {
|
|||||||
if sidebarQueue {
|
if sidebarQueue {
|
||||||
PlayerQueueView(sidebarQueue: true, fullScreen: $fullScreenDetails)
|
PlayerQueueView(sidebarQueue: true, fullScreen: $fullScreenDetails)
|
||||||
.frame(maxWidth: 350)
|
.frame(maxWidth: 350)
|
||||||
.transition(.move(edge: .trailing))
|
|
||||||
.background(colorScheme == .dark ? Color.black : Color.white)
|
.background(colorScheme == .dark ? Color.black : Color.white)
|
||||||
|
.transition(.move(edge: .bottom))
|
||||||
}
|
}
|
||||||
#elseif os(macOS)
|
#elseif os(macOS)
|
||||||
if Defaults[.playerSidebar] != .never {
|
if Defaults[.playerSidebar] != .never {
|
||||||
@ -366,6 +365,12 @@ struct VideoPlayerView: View {
|
|||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
var playerDragGesture: some Gesture {
|
var playerDragGesture: some Gesture {
|
||||||
DragGesture(minimumDistance: 0, coordinateSpace: .global)
|
DragGesture(minimumDistance: 0, coordinateSpace: .global)
|
||||||
|
.updating($dragGestureOffset) { value, state, _ in
|
||||||
|
state = value.translation.height > 0 ? value.translation : .zero
|
||||||
|
}
|
||||||
|
.updating($dragGestureState) { _, state, _ in
|
||||||
|
state = true
|
||||||
|
}
|
||||||
.onChanged { value in
|
.onChanged { value in
|
||||||
guard player.presentingPlayer,
|
guard player.presentingPlayer,
|
||||||
!playerControls.presentingControlsOverlay else { return }
|
!playerControls.presentingControlsOverlay else { return }
|
||||||
@ -378,33 +383,46 @@ struct VideoPlayerView: View {
|
|||||||
|
|
||||||
guard drag > 0 else { return }
|
guard drag > 0 else { return }
|
||||||
|
|
||||||
|
viewDragOffset = drag
|
||||||
|
|
||||||
if drag > 60,
|
if drag > 60,
|
||||||
player.playingFullScreen,
|
player.playingFullScreen
|
||||||
!OrientationTracker.shared.currentInterfaceOrientation.isLandscape
|
|
||||||
{
|
{
|
||||||
player.exitFullScreen()
|
player.exitFullScreen()
|
||||||
player.lockedOrientation = nil
|
if Defaults[.rotateToPortraitOnExitFullScreen] {
|
||||||
|
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: .portrait)
|
||||||
|
playerControls.show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
viewVerticalOffset = drag
|
|
||||||
}
|
}
|
||||||
.onEnded { _ in
|
.onEnded { _ in
|
||||||
guard player.presentingPlayer,
|
onPlayerDragGestureEnded()
|
||||||
!playerControls.presentingControlsOverlay else { return }
|
|
||||||
if viewVerticalOffset > 100 {
|
|
||||||
player.backend.setNeedsDrawing(false)
|
|
||||||
player.hide()
|
|
||||||
player.exitFullScreen()
|
|
||||||
} else {
|
|
||||||
viewVerticalOffset = 0
|
|
||||||
player.backend.setNeedsDrawing(true)
|
|
||||||
player.show()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func onPlayerDragGestureEnded() {
|
||||||
|
guard player.presentingPlayer,
|
||||||
|
!playerControls.presentingControlsOverlay else { return }
|
||||||
|
|
||||||
|
if viewDragOffset > 100 {
|
||||||
|
player.hide()
|
||||||
|
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
|
||||||
|
player.backend.setNeedsDrawing(false)
|
||||||
|
player.exitFullScreen()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
withAnimation(.linear(duration: 0.2)) {
|
||||||
|
viewDragOffset = 0
|
||||||
|
}
|
||||||
|
player.backend.setNeedsDrawing(true)
|
||||||
|
player.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func configureOrientationUpdatesBasedOnAccelerometer() {
|
private func configureOrientationUpdatesBasedOnAccelerometer() {
|
||||||
if OrientationTracker.shared.currentInterfaceOrientation.isLandscape,
|
let currentOrientation = OrientationTracker.shared.currentInterfaceOrientation
|
||||||
|
if currentOrientation.isLandscape,
|
||||||
Defaults[.enterFullscreenInLandscape],
|
Defaults[.enterFullscreenInLandscape],
|
||||||
!player.playingFullScreen,
|
!player.playingFullScreen,
|
||||||
!player.playingInPictureInPicture
|
!player.playingInPictureInPicture
|
||||||
@ -413,6 +431,8 @@ struct VideoPlayerView: View {
|
|||||||
player.controls.presentingControls = false
|
player.controls.presentingControls = false
|
||||||
player.enterFullScreen(showControls: false)
|
player.enterFullScreen(showControls: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Orientation.lockOrientation(.allButUpsideDown, andRotateTo: currentOrientation)
|
||||||
}
|
}
|
||||||
|
|
||||||
orientationObserver = NotificationCenter.default.addObserver(
|
orientationObserver = NotificationCenter.default.addObserver(
|
||||||
|
@ -63,6 +63,8 @@ struct ControlsBar: View {
|
|||||||
}
|
}
|
||||||
} else if detailsToggleFullScreen {
|
} else if detailsToggleFullScreen {
|
||||||
Button {
|
Button {
|
||||||
|
playerControls.presentingControlsOverlay = false
|
||||||
|
playerControls.presentingControls = false
|
||||||
withAnimation {
|
withAnimation {
|
||||||
fullScreen.toggle()
|
fullScreen.toggle()
|
||||||
}
|
}
|
||||||
|
@ -237,5 +237,7 @@ struct YatteeApp: App {
|
|||||||
#else
|
#else
|
||||||
player.updateRemoteCommandCenter()
|
player.updateRemoteCommandCenter()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
player.presentingPlayer = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user