From eebca5ca59bfa7c1a7862ead58f2ad3d3cedc9ca Mon Sep 17 00:00:00 2001 From: Arkadiusz Fal Date: Fri, 2 Sep 2022 02:06:33 +0200 Subject: [PATCH] Fix tvOS controls overlay buttons --- Shared/Player/Controls/ControlsOverlay.swift | 19 +++++++------ Shared/Player/StreamControl.swift | 25 +++++++++-------- Yattee.xcodeproj/project.pbxproj | 4 +++ tvOS/ControlsOverlayButton.swift | 28 ++++++++++++++++++++ 4 files changed, 57 insertions(+), 19 deletions(-) create mode 100644 tvOS/ControlsOverlayButton.swift diff --git a/Shared/Player/Controls/ControlsOverlay.swift b/Shared/Player/Controls/ControlsOverlay.swift index 5b4facc2..a87cfe98 100644 --- a/Shared/Player/Controls/ControlsOverlay.swift +++ b/Shared/Player/Controls/ControlsOverlay.swift @@ -91,6 +91,13 @@ struct ControlsOverlay: View { #if os(tvOS) .padding(.horizontal, 40) #endif + + #if os(tvOS) + Text("Press and hold remote button to open captions and quality menus") + .frame(maxWidth: 400) + .font(.caption) + .foregroundColor(.secondary) + #endif } .frame(maxHeight: overlayHeight) #if os(tvOS) @@ -105,7 +112,7 @@ struct ControlsOverlay: View { private var overlayHeight: Double { #if os(tvOS) - contentSize.height + 50.0 + contentSize.height + 80.0 #else contentSize.height #endif @@ -255,9 +262,7 @@ struct ControlsOverlay: View { .modifier(ControlBackgroundModifier()) .mask(RoundedRectangle(cornerRadius: 3)) #else - Button { - presentingButtonHintAlert = true - } label: { + ControlsOverlayButton(focusedField: $focusedField, field: .qualityProfile) { Text(player.qualityProfileSelection?.description ?? "Automatic") .lineLimit(1) .frame(maxWidth: 320) @@ -309,7 +314,7 @@ struct ControlsOverlay: View { .modifier(ControlBackgroundModifier()) .mask(RoundedRectangle(cornerRadius: 3)) #else - StreamControl(presentingButtonHintAlert: $presentingButtonHintAlert) + StreamControl(focusedField: $focusedField) #endif } @@ -339,9 +344,7 @@ struct ControlsOverlay: View { .modifier(ControlBackgroundModifier()) .mask(RoundedRectangle(cornerRadius: 3)) #else - Button { - presentingButtonHintAlert = true - } label: { + ControlsOverlayButton(focusedField: $focusedField, field: .captions) { HStack(spacing: 8) { Image(systemName: "text.bubble") if let captions = captionsBinding.wrappedValue { diff --git a/Shared/Player/StreamControl.swift b/Shared/Player/StreamControl.swift index 98224bc6..59b0a540 100644 --- a/Shared/Player/StreamControl.swift +++ b/Shared/Player/StreamControl.swift @@ -1,14 +1,16 @@ import SwiftUI struct StreamControl: View { - @Binding var presentingButtonHintAlert: Bool + #if os(tvOS) + var focusedField: FocusState.Binding? + + init(focusedField: FocusState.Binding?) { + self.focusedField = focusedField + } + #endif @EnvironmentObject private var player - init(presentingButtonHintAlert: Binding = .constant(false)) { - _presentingButtonHintAlert = presentingButtonHintAlert - } - var body: some View { Group { #if !os(tvOS) @@ -36,9 +38,7 @@ struct StreamControl: View { .disabled(player.isLoadingAvailableStreams) #endif #else - Button { - presentingButtonHintAlert = true - } label: { + ControlsOverlayButton(focusedField: focusedField!, field: .stream) { Text(player.streamSelection?.shortQuality ?? "loading") .frame(maxWidth: 320) } @@ -51,7 +51,6 @@ struct StreamControl: View { } #endif } - .transaction { t in t.animation = .none } .onChange(of: player.streamSelection) { selection in guard let selection = selection else { return } @@ -72,7 +71,11 @@ struct StreamControl: View { struct StreamControl_Previews: PreviewProvider { static var previews: some View { - StreamControl() - .injectFixtureEnvironmentObjects() + #if os(tvOS) + StreamControl(focusedField: .none) + .injectFixtureEnvironmentObjects() + #else + StreamControl() + #endif } } diff --git a/Yattee.xcodeproj/project.pbxproj b/Yattee.xcodeproj/project.pbxproj index 3cebf029..0efbab61 100644 --- a/Yattee.xcodeproj/project.pbxproj +++ b/Yattee.xcodeproj/project.pbxproj @@ -748,6 +748,7 @@ 37D6025928C17375009E8D98 /* PlaybackStatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D6025828C17375009E8D98 /* PlaybackStatsView.swift */; }; 37D6025A28C17375009E8D98 /* PlaybackStatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D6025828C17375009E8D98 /* PlaybackStatsView.swift */; }; 37D6025B28C17375009E8D98 /* PlaybackStatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D6025828C17375009E8D98 /* PlaybackStatsView.swift */; }; + 37D6025D28C17719009E8D98 /* ControlsOverlayButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D6025C28C17719009E8D98 /* ControlsOverlayButton.swift */; }; 37DD87C7271C9CFE0027CBF9 /* PlayerStreams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD87C6271C9CFE0027CBF9 /* PlayerStreams.swift */; }; 37DD87C8271C9CFE0027CBF9 /* PlayerStreams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD87C6271C9CFE0027CBF9 /* PlayerStreams.swift */; }; 37DD87C9271C9CFE0027CBF9 /* PlayerStreams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DD87C6271C9CFE0027CBF9 /* PlayerStreams.swift */; }; @@ -1264,6 +1265,7 @@ 37D4B1AE26729DEB00C925CA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 37D526DD2720AC4400ED2F5E /* VideosAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideosAPI.swift; sourceTree = ""; }; 37D6025828C17375009E8D98 /* PlaybackStatsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaybackStatsView.swift; sourceTree = ""; }; + 37D6025C28C17719009E8D98 /* ControlsOverlayButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlsOverlayButton.swift; sourceTree = ""; }; 37D9169A27388A81002B1BAA /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 37DD87C6271C9CFE0027CBF9 /* PlayerStreams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerStreams.swift; sourceTree = ""; }; 37DD9DA22785BBC900539416 /* NoCommentsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoCommentsView.swift; sourceTree = ""; }; @@ -2086,6 +2088,7 @@ isa = PBXGroup; children = ( 37666BA927023AF000F869E5 /* AccountSelectionView.swift */, + 37D6025C28C17719009E8D98 /* ControlsOverlayButton.swift */, 37FADFFF272ED58000330459 /* EditFavorites.swift */, 3730D89F2712E2B70020ED53 /* NowPlayingView.swift */, 37BAB54B269B39FD00E75ED1 /* TVNavigationView.swift */, @@ -3341,6 +3344,7 @@ 3748187026A769D60084E870 /* DetailBadge.swift in Sources */, 3741A32C27E7EFFD00D266D1 /* PlayerControls.swift in Sources */, 371B7E632759706A00D21217 /* CommentsView.swift in Sources */, + 37D6025D28C17719009E8D98 /* ControlsOverlayButton.swift in Sources */, 37D2E0D628B67EFC00F64D52 /* Delay.swift in Sources */, 379F1421289ECE7F00DE48B5 /* QualitySettings.swift in Sources */, 37A9965C26D6F8CA006E3224 /* HorizontalCells.swift in Sources */, diff --git a/tvOS/ControlsOverlayButton.swift b/tvOS/ControlsOverlayButton.swift new file mode 100644 index 00000000..1bc2ac6e --- /dev/null +++ b/tvOS/ControlsOverlayButton.swift @@ -0,0 +1,28 @@ +import SwiftUI + +struct ControlsOverlayButton: View { + var focusedField: FocusState.Binding + var field: ControlsOverlay.Field + let label: LabelView + + init( + focusedField: FocusState.Binding, + field: ControlsOverlay.Field, + @ViewBuilder label: @escaping () -> LabelView + ) { + self.focusedField = focusedField + self.field = field + self.label = label() + } + + var body: some View { + label + .padding() + .frame(width: 400) + .focusable() + .focused(focusedField, equals: field) + .background(focusedField.wrappedValue == field ? Color.white : Color.secondary) + .foregroundColor(focusedField.wrappedValue == field ? Color.black : Color.white) + .clipShape(RoundedRectangle(cornerRadius: 4)) + } +}