mirror of
https://github.com/yattee/yattee.git
synced 2024-12-22 21:43:41 +00:00
Minor tvOS controls and remote improvements
This commit is contained in:
parent
1645c81e00
commit
97e9889682
@ -1,3 +1,4 @@
|
|||||||
|
import Combine
|
||||||
import CoreMedia
|
import CoreMedia
|
||||||
import Foundation
|
import Foundation
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
@ -9,6 +10,10 @@ final class PlayerControlsModel: ObservableObject {
|
|||||||
@Published var presentingControlsOverlay = false { didSet { handleOverlayPresentationChange() } }
|
@Published var presentingControlsOverlay = false { didSet { handleOverlayPresentationChange() } }
|
||||||
@Published var timer: Timer?
|
@Published var timer: Timer?
|
||||||
|
|
||||||
|
#if os(tvOS)
|
||||||
|
var reporter = PassthroughSubject<String, Never>()
|
||||||
|
#endif
|
||||||
|
|
||||||
var player: PlayerModel!
|
var player: PlayerModel!
|
||||||
|
|
||||||
init(
|
init(
|
||||||
|
@ -372,7 +372,14 @@ final class PlayerModel: ObservableObject {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in
|
DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in
|
||||||
self?.backend.setNeedsDrawing(self?.presentingPlayer ?? false)
|
guard let self = self else { return }
|
||||||
|
self.backend.setNeedsDrawing(self.presentingPlayer)
|
||||||
|
|
||||||
|
#if os(tvOS)
|
||||||
|
if self.presentingPlayer {
|
||||||
|
self.controls.show()
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
controls.hide()
|
controls.hide()
|
||||||
|
@ -70,7 +70,7 @@ struct PlayerControls: View {
|
|||||||
.zIndex(1)
|
.zIndex(1)
|
||||||
}
|
}
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
.offset(y: -100)
|
.offset(y: -50)
|
||||||
#endif
|
#endif
|
||||||
.frame(maxWidth: 500)
|
.frame(maxWidth: 500)
|
||||||
.padding(.bottom, 2)
|
.padding(.bottom, 2)
|
||||||
|
77
Shared/Player/Controls/TVControls.swift
Normal file
77
Shared/Player/Controls/TVControls.swift
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import Combine
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct TVControls: UIViewRepresentable {
|
||||||
|
var model: PlayerControlsModel!
|
||||||
|
var player: PlayerModel!
|
||||||
|
var thumbnails: ThumbnailsModel!
|
||||||
|
|
||||||
|
@State private var direction = ""
|
||||||
|
@State private var controlsArea = UIView()
|
||||||
|
|
||||||
|
func makeUIView(context: Context) -> UIView {
|
||||||
|
let tapGesture = UITapGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.handleTap(sender:)))
|
||||||
|
|
||||||
|
let leftSwipe = UISwipeGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.handleSwipe(sender:)))
|
||||||
|
leftSwipe.direction = .left
|
||||||
|
|
||||||
|
let rightSwipe = UISwipeGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.handleSwipe(sender:)))
|
||||||
|
rightSwipe.direction = .right
|
||||||
|
|
||||||
|
let upSwipe = UISwipeGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.handleSwipe(sender:)))
|
||||||
|
upSwipe.direction = .up
|
||||||
|
|
||||||
|
let downSwipe = UISwipeGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.handleSwipe(sender:)))
|
||||||
|
downSwipe.direction = .down
|
||||||
|
|
||||||
|
controlsArea.addGestureRecognizer(tapGesture)
|
||||||
|
controlsArea.addGestureRecognizer(leftSwipe)
|
||||||
|
controlsArea.addGestureRecognizer(rightSwipe)
|
||||||
|
controlsArea.addGestureRecognizer(upSwipe)
|
||||||
|
controlsArea.addGestureRecognizer(downSwipe)
|
||||||
|
|
||||||
|
let controls = UIHostingController(rootView: PlayerControls(player: player, thumbnails: thumbnails))
|
||||||
|
controls.view.frame = .init(
|
||||||
|
origin: .zero,
|
||||||
|
size: .init(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
|
||||||
|
)
|
||||||
|
|
||||||
|
controlsArea.addSubview(controls.view)
|
||||||
|
|
||||||
|
return controlsArea
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUIView(_: UIView, context _: Context) {}
|
||||||
|
|
||||||
|
func makeCoordinator() -> TVControls.Coordinator {
|
||||||
|
Coordinator(controlsArea, model: model)
|
||||||
|
}
|
||||||
|
|
||||||
|
final class Coordinator: NSObject {
|
||||||
|
private let view: UIView
|
||||||
|
private let model: PlayerControlsModel
|
||||||
|
|
||||||
|
init(_ view: UIView, model: PlayerControlsModel) {
|
||||||
|
self.view = view
|
||||||
|
self.model = model
|
||||||
|
super.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(*, unavailable)
|
||||||
|
required init?(coder _: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func handleTap(sender: UITapGestureRecognizer) {
|
||||||
|
let location = sender.location(in: view)
|
||||||
|
model.reporter.send("tap \(location)")
|
||||||
|
print("tap \(location)")
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func handleSwipe(sender: UISwipeGestureRecognizer) {
|
||||||
|
let location = sender.location(in: view)
|
||||||
|
model.reporter.send("swipe \(location)")
|
||||||
|
print("swipe \(location)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -138,29 +138,39 @@ struct VideoPlayerView: View {
|
|||||||
Group {
|
Group {
|
||||||
ZStack(alignment: .bottomLeading) {
|
ZStack(alignment: .bottomLeading) {
|
||||||
#if os(tvOS)
|
#if os(tvOS)
|
||||||
playerView
|
ZStack {
|
||||||
.ignoresSafeArea(.all, edges: .all)
|
playerView
|
||||||
.onMoveCommand { direction in
|
|
||||||
if direction == .up || direction == .down {
|
|
||||||
playerControls.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
playerControls.resetTimer()
|
tvControls
|
||||||
|
}
|
||||||
guard !playerControls.presentingControls else {
|
.ignoresSafeArea(.all, edges: .all)
|
||||||
return
|
.onMoveCommand { direction in
|
||||||
}
|
if direction == .up || direction == .down {
|
||||||
|
playerControls.show()
|
||||||
if direction == .left {
|
|
||||||
player.backend.seek(relative: .secondsInDefaultTimescale(-10))
|
|
||||||
}
|
|
||||||
if direction == .right {
|
|
||||||
player.backend.seek(relative: .secondsInDefaultTimescale(10))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.onPlayPauseCommand {
|
|
||||||
player.togglePlay()
|
playerControls.resetTimer()
|
||||||
|
|
||||||
|
guard !playerControls.presentingControls else { return }
|
||||||
|
|
||||||
|
if direction == .left {
|
||||||
|
player.backend.seek(relative: .secondsInDefaultTimescale(-10))
|
||||||
}
|
}
|
||||||
|
if direction == .right {
|
||||||
|
player.backend.seek(relative: .secondsInDefaultTimescale(10))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onPlayPauseCommand {
|
||||||
|
player.togglePlay()
|
||||||
|
}
|
||||||
|
|
||||||
|
.onExitCommand {
|
||||||
|
if playerControls.presentingControls {
|
||||||
|
playerControls.hide()
|
||||||
|
} else {
|
||||||
|
player.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
GeometryReader { geometry in
|
GeometryReader { geometry in
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
@ -308,9 +318,8 @@ struct VideoPlayerView: View {
|
|||||||
|
|
||||||
#if !os(tvOS)
|
#if !os(tvOS)
|
||||||
PlayerGestures()
|
PlayerGestures()
|
||||||
|
PlayerControls(player: player, thumbnails: thumbnails)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
PlayerControls(player: player, thumbnails: thumbnails)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -502,6 +511,16 @@ struct VideoPlayerView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if os(tvOS)
|
||||||
|
var tvControls: some View {
|
||||||
|
TVControls(model: playerControls, player: player, thumbnails: thumbnails)
|
||||||
|
.onReceive(playerControls.reporter) { _ in
|
||||||
|
playerControls.show()
|
||||||
|
playerControls.resetTimer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VideoPlayerView_Previews: PreviewProvider {
|
struct VideoPlayerView_Previews: PreviewProvider {
|
||||||
|
@ -343,6 +343,7 @@
|
|||||||
3761ABFF26F0F8DE00AA496F /* EnvironmentValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3761ABFC26F0F8DE00AA496F /* EnvironmentValues.swift */; };
|
3761ABFF26F0F8DE00AA496F /* EnvironmentValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3761ABFC26F0F8DE00AA496F /* EnvironmentValues.swift */; };
|
||||||
3763495126DFF59D00B9A393 /* AppSidebarRecents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3763495026DFF59D00B9A393 /* AppSidebarRecents.swift */; };
|
3763495126DFF59D00B9A393 /* AppSidebarRecents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3763495026DFF59D00B9A393 /* AppSidebarRecents.swift */; };
|
||||||
3763495226DFF59D00B9A393 /* AppSidebarRecents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3763495026DFF59D00B9A393 /* AppSidebarRecents.swift */; };
|
3763495226DFF59D00B9A393 /* AppSidebarRecents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3763495026DFF59D00B9A393 /* AppSidebarRecents.swift */; };
|
||||||
|
37648B69286CF5F1003D330B /* TVControls.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37648B68286CF5F1003D330B /* TVControls.swift */; };
|
||||||
376527BB285F60F700102284 /* PlayerTimeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 376527BA285F60F700102284 /* PlayerTimeModel.swift */; };
|
376527BB285F60F700102284 /* PlayerTimeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 376527BA285F60F700102284 /* PlayerTimeModel.swift */; };
|
||||||
376527BC285F60F700102284 /* PlayerTimeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 376527BA285F60F700102284 /* PlayerTimeModel.swift */; };
|
376527BC285F60F700102284 /* PlayerTimeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 376527BA285F60F700102284 /* PlayerTimeModel.swift */; };
|
||||||
376527BD285F60F700102284 /* PlayerTimeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 376527BA285F60F700102284 /* PlayerTimeModel.swift */; };
|
376527BD285F60F700102284 /* PlayerTimeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 376527BA285F60F700102284 /* PlayerTimeModel.swift */; };
|
||||||
@ -969,6 +970,7 @@
|
|||||||
375E45F727B1AC4700BA7902 /* PlayerControlsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerControlsModel.swift; sourceTree = "<group>"; };
|
375E45F727B1AC4700BA7902 /* PlayerControlsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerControlsModel.swift; sourceTree = "<group>"; };
|
||||||
3761ABFC26F0F8DE00AA496F /* EnvironmentValues.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnvironmentValues.swift; sourceTree = "<group>"; };
|
3761ABFC26F0F8DE00AA496F /* EnvironmentValues.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnvironmentValues.swift; sourceTree = "<group>"; };
|
||||||
3763495026DFF59D00B9A393 /* AppSidebarRecents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSidebarRecents.swift; sourceTree = "<group>"; };
|
3763495026DFF59D00B9A393 /* AppSidebarRecents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSidebarRecents.swift; sourceTree = "<group>"; };
|
||||||
|
37648B68286CF5F1003D330B /* TVControls.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVControls.swift; sourceTree = "<group>"; };
|
||||||
376527BA285F60F700102284 /* PlayerTimeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerTimeModel.swift; sourceTree = "<group>"; };
|
376527BA285F60F700102284 /* PlayerTimeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerTimeModel.swift; sourceTree = "<group>"; };
|
||||||
376578842685429C00D4EA09 /* CaseIterable+Next.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CaseIterable+Next.swift"; sourceTree = "<group>"; };
|
376578842685429C00D4EA09 /* CaseIterable+Next.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CaseIterable+Next.swift"; sourceTree = "<group>"; };
|
||||||
376578882685471400D4EA09 /* Playlist.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Playlist.swift; sourceTree = "<group>"; };
|
376578882685471400D4EA09 /* Playlist.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Playlist.swift; sourceTree = "<group>"; };
|
||||||
@ -1363,9 +1365,10 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
3756C2A428610F6D00E4B059 /* OSD */,
|
3756C2A428610F6D00E4B059 /* OSD */,
|
||||||
37030FFE27B04DCC00ECDDAA /* PlayerControls.swift */,
|
|
||||||
37A5DBC7285E371400CA4DD1 /* ControlBackgroundModifier.swift */,
|
37A5DBC7285E371400CA4DD1 /* ControlBackgroundModifier.swift */,
|
||||||
37F13B61285E43C000B137E4 /* ControlsOverlay.swift */,
|
37F13B61285E43C000B137E4 /* ControlsOverlay.swift */,
|
||||||
|
37030FFE27B04DCC00ECDDAA /* PlayerControls.swift */,
|
||||||
|
37648B68286CF5F1003D330B /* TVControls.swift */,
|
||||||
);
|
);
|
||||||
path = Controls;
|
path = Controls;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -3016,6 +3019,7 @@
|
|||||||
37F49BA526CAA59B00304AC0 /* Playlist+Fixtures.swift in Sources */,
|
37F49BA526CAA59B00304AC0 /* Playlist+Fixtures.swift in Sources */,
|
||||||
376CD21826FBE18D001E1AC1 /* Instance+Fixtures.swift in Sources */,
|
376CD21826FBE18D001E1AC1 /* Instance+Fixtures.swift in Sources */,
|
||||||
37CEE4BF2677B670005A1EFE /* SingleAssetStream.swift in Sources */,
|
37CEE4BF2677B670005A1EFE /* SingleAssetStream.swift in Sources */,
|
||||||
|
37648B69286CF5F1003D330B /* TVControls.swift in Sources */,
|
||||||
374C053D2724614F009BDDBE /* PlayerTVMenu.swift in Sources */,
|
374C053D2724614F009BDDBE /* PlayerTVMenu.swift in Sources */,
|
||||||
37BE0BD426A1D47D0092E2DB /* AppleAVPlayerView.swift in Sources */,
|
37BE0BD426A1D47D0092E2DB /* AppleAVPlayerView.swift in Sources */,
|
||||||
37977585268922F600DD52A8 /* InvidiousAPI.swift in Sources */,
|
37977585268922F600DD52A8 /* InvidiousAPI.swift in Sources */,
|
||||||
|
Loading…
Reference in New Issue
Block a user