Make every tvOS switcher a menu and retire the full-screen picker

The header button and the settings row both raised a fullScreenCover to
pick a context; a Menu offers the same rows in place for one click,
matching the pickers elsewhere in Settings. With every presentation now
a menu, the picker view and the rows' inMenu flag had nothing left to
serve. The subscriptions header also cedes its width to the account
name: the view-options button keeps only its icon, since two full
titles wrapped into tall two-line lozenges in the tvOS sidebar.
This commit is contained in:
Arkadiusz Fal
2026-09-14 18:59:00 +02:00
parent ded53d7962
commit 50e32dbe5a
2 changed files with 20 additions and 55 deletions

View File

@@ -63,9 +63,6 @@ struct DataContextSwitcherRows: View {
}
}
.disabled(isRefused(context))
#if os(tvOS)
.buttonStyle(TVSettingsButtonStyle())
#endif
#endif
}
@@ -387,21 +384,16 @@ struct DataContextSwitcherMenu: View {
/// A settings row naming the current context, with the switcher attached.
struct DataContextSwitcherSettingsRow: View {
@Environment(\.appEnvironment) private var appEnvironment
#if os(tvOS)
@State private var showingPicker = false
#endif
var body: some View {
#if os(tvOS)
Button {
showingPicker = true
// The same menu the Subscriptions, History and Playlists headers use:
// one click, options in place, rather than a full-screen round trip.
Menu {
DataContextSwitcherRows()
} label: {
rowLabel
}
.buttonStyle(TVSettingsButtonStyle())
.fullScreenCover(isPresented: $showingPicker) {
DataContextSwitcherPicker()
}
#elseif os(macOS)
// A macOS menu label renders its first text and nothing else — the
// trailing name was dropped at every width, leaving the row silent
@@ -498,62 +490,29 @@ struct DataContextSwitcherSettingsRow: View {
}
}
// MARK: - tvOS picker
// MARK: - tvOS button
#if os(tvOS)
/// The tvOS switcher, presented in a `fullScreenCover`: menus are unusable
/// there, and a sheet is a card too small to read.
struct DataContextSwitcherPicker: View {
@Environment(\.appEnvironment) private var appEnvironment
@Environment(\.dismiss) private var dismiss
var body: some View {
ZStack {
Color.black.ignoresSafeArea()
VStack(alignment: .leading, spacing: 24) {
Text(String(localized: "accounts.switcher.title"))
.font(.title2.bold())
ScrollView {
VStack(alignment: .leading, spacing: 12) {
DataContextSwitcherRows {
dismiss()
}
}
}
Button(String(localized: "common.close")) {
dismiss()
}
.buttonStyle(TVSettingsButtonStyle())
}
.frame(maxWidth: 900)
.padding(.horizontal, 80)
.padding(.vertical, 60)
}
}
}
/// The button that presents the picker, for placing beside other tvOS controls.
/// The switcher for placing beside other tvOS controls: a menu, like the
/// menu-style pickers in Settings, so switching is one click instead of a
/// full-screen round trip.
struct DataContextSwitcherButton: View {
@Environment(\.appEnvironment) private var appEnvironment
@State private var showingPicker = false
var body: some View {
Button {
showingPicker = true
Menu {
DataContextSwitcherRows()
} label: {
Label {
// One line always: beside other header buttons the name would
// wrap into a tall two-line lozenge before it truncates.
Text(DataContextSwitcherLabels.title(for: currentContext, in: appEnvironment))
.lineLimit(1)
.truncationMode(.tail)
} icon: {
DataContextSwitcherIcon()
}
}
.buttonStyle(.bordered)
.fullScreenCover(isPresented: $showingPicker) {
DataContextSwitcherPicker()
}
}
private var currentContext: DataContext {

View File

@@ -906,12 +906,18 @@ struct SubscriptionsView: View {
#if os(tvOS)
private var tvOSViewOptionsButton: some View {
HStack(spacing: 16) {
// The sidebar is too narrow for both buttons' full titles, and
// tvOS wraps them into tall two-line lozenges before truncating.
// The account name is the label worth keeping whole, so this
// button keeps only its icon and the name gets the width.
Button {
showViewOptions = true
} label: {
Label(String(localized: "viewOptions.title"), systemImage: "slider.horizontal.3")
.labelStyle(.iconOnly)
}
.buttonStyle(.bordered)
.accessibilityLabel(Text(String(localized: "viewOptions.title")))
if DataContextSwitcherVisibility.showsPickers(in: appEnvironment) {
DataContextSwitcherButton()