mirror of
https://github.com/yattee/yattee.git
synced 2025-11-12 21:28:42 +00:00
Improve tvOS settings UI styling and navigation
- Add TVOSPlainToggleStyle for cleaner toggle appearance on tvOS - Remove focus overlays from settings navigation links and buttons - Apply plain button and list styles across all settings screens - Implement custom system controls picker for tvOS to avoid focus overlay - Update SettingsPickerModifier with platform-specific styling 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -43,7 +43,7 @@ struct HomeView: View {
|
||||
AccentButton(imageSystemName: "ellipsis") {
|
||||
NavigationModel.shared.presentingOpenVideos = true
|
||||
}
|
||||
.frame(maxWidth: 40)
|
||||
.frame(maxWidth: 40)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -74,6 +74,7 @@ struct HomeView: View {
|
||||
#if os(tvOS)
|
||||
.font(.caption)
|
||||
.imageScale(.small)
|
||||
.foregroundColor(.primary)
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -31,7 +31,9 @@ struct AdvancedSettings: View {
|
||||
List {
|
||||
advancedSettings
|
||||
}
|
||||
#if os(iOS)
|
||||
#if os(tvOS)
|
||||
.listStyle(.plain)
|
||||
#elseif os(iOS)
|
||||
.sheet(isPresented: $presentingShareSheet) {
|
||||
ShareSheet(activityItems: filesToShare)
|
||||
.id("logs-\(filesToShare.count)")
|
||||
@@ -41,6 +43,8 @@ struct AdvancedSettings: View {
|
||||
#endif
|
||||
}
|
||||
#if os(tvOS)
|
||||
.buttonStyle(.plain)
|
||||
.toggleStyle(TVOSPlainToggleStyle())
|
||||
.frame(maxWidth: 1000)
|
||||
#endif
|
||||
.navigationTitle("Advanced")
|
||||
|
||||
@@ -52,10 +52,14 @@ struct BrowsingSettings: View {
|
||||
}
|
||||
#if os(iOS)
|
||||
.listStyle(.insetGrouped)
|
||||
#elseif os(tvOS)
|
||||
.listStyle(.plain)
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#if os(tvOS)
|
||||
.buttonStyle(.plain)
|
||||
.toggleStyle(TVOSPlainToggleStyle())
|
||||
.frame(maxWidth: 1200)
|
||||
#else
|
||||
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
|
||||
@@ -111,6 +115,9 @@ struct BrowsingSettings: View {
|
||||
NavigationLink(destination: LazyView(HomeSettings())) {
|
||||
Text("Home Settings")
|
||||
}
|
||||
#if os(tvOS)
|
||||
.buttonStyle(.plain)
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,9 +29,16 @@ struct HistorySettings: View {
|
||||
List {
|
||||
sections
|
||||
}
|
||||
#if os(tvOS)
|
||||
.listStyle(.plain)
|
||||
#elseif os(iOS)
|
||||
.listStyle(.insetGrouped)
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#if os(tvOS)
|
||||
.buttonStyle(.plain)
|
||||
.toggleStyle(TVOSPlainToggleStyle())
|
||||
.frame(maxWidth: 1000)
|
||||
#elseif os(iOS)
|
||||
.listStyle(.insetGrouped)
|
||||
|
||||
@@ -22,7 +22,9 @@ struct LocationsSettings: View {
|
||||
List {
|
||||
settings
|
||||
}
|
||||
#if os(iOS)
|
||||
#if os(tvOS)
|
||||
.listStyle(.plain)
|
||||
#elseif os(iOS)
|
||||
.listStyle(.insetGrouped)
|
||||
#endif
|
||||
#endif
|
||||
@@ -42,6 +44,8 @@ struct LocationsSettings: View {
|
||||
InstanceForm(savedInstanceID: $savedFormInstanceID)
|
||||
}
|
||||
#if os(tvOS)
|
||||
.buttonStyle(.plain)
|
||||
.toggleStyle(TVOSPlainToggleStyle())
|
||||
.frame(maxWidth: 1000)
|
||||
#endif
|
||||
.navigationTitle("Locations")
|
||||
|
||||
@@ -53,9 +53,16 @@ struct PlayerControlsSettings: View {
|
||||
List {
|
||||
sections
|
||||
}
|
||||
#if os(tvOS)
|
||||
.listStyle(.plain)
|
||||
#elseif os(iOS)
|
||||
.listStyle(.insetGrouped)
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#if os(tvOS)
|
||||
.buttonStyle(.plain)
|
||||
.toggleStyle(TVOSPlainToggleStyle())
|
||||
.frame(maxWidth: 1000)
|
||||
#elseif os(iOS)
|
||||
.listStyle(.insetGrouped)
|
||||
@@ -143,6 +150,45 @@ struct PlayerControlsSettings: View {
|
||||
#endif
|
||||
}
|
||||
|
||||
#if os(tvOS)
|
||||
// Custom implementation for tvOS to avoid focus overlay
|
||||
return VStack(alignment: .leading, spacing: 0) {
|
||||
Text("System controls buttons")
|
||||
.font(.headline)
|
||||
.padding(.vertical, 8)
|
||||
|
||||
Button(action: { systemControlsCommands = .seek }) {
|
||||
HStack {
|
||||
Text(labelText("Seek".localized()))
|
||||
Spacer()
|
||||
if systemControlsCommands == .seek {
|
||||
Image(systemName: "checkmark")
|
||||
.foregroundColor(.accentColor)
|
||||
}
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.padding(.vertical, 4)
|
||||
|
||||
Button(action: {
|
||||
systemControlsCommands = .restartAndAdvanceToNext
|
||||
player.updateRemoteCommandCenter()
|
||||
}) {
|
||||
HStack {
|
||||
Text(labelText("Restart/Play next".localized()))
|
||||
Spacer()
|
||||
if systemControlsCommands == .restartAndAdvanceToNext {
|
||||
Image(systemName: "checkmark")
|
||||
.foregroundColor(.accentColor)
|
||||
}
|
||||
}
|
||||
.contentShape(Rectangle())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.padding(.vertical, 4)
|
||||
}
|
||||
#else
|
||||
return Picker("System controls buttons", selection: $systemControlsCommands) {
|
||||
Text(labelText("Seek".localized())).tag(SystemControlsCommands.seek)
|
||||
Text(labelText("Restart/Play next".localized())).tag(SystemControlsCommands.restartAndAdvanceToNext)
|
||||
@@ -151,6 +197,7 @@ struct PlayerControlsSettings: View {
|
||||
player.updateRemoteCommandCenter()
|
||||
}
|
||||
.modifier(SettingsPickerModifier())
|
||||
#endif
|
||||
}
|
||||
|
||||
@ViewBuilder private var controlsLayoutFooter: some View {
|
||||
|
||||
@@ -69,9 +69,16 @@ struct PlayerSettings: View {
|
||||
List {
|
||||
sections
|
||||
}
|
||||
#if os(tvOS)
|
||||
.listStyle(.plain)
|
||||
#elseif os(iOS)
|
||||
.listStyle(.insetGrouped)
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#if os(tvOS)
|
||||
.buttonStyle(.plain)
|
||||
.toggleStyle(TVOSPlainToggleStyle())
|
||||
.frame(maxWidth: 1000)
|
||||
#elseif os(iOS)
|
||||
.listStyle(.insetGrouped)
|
||||
|
||||
@@ -24,12 +24,19 @@ struct QualitySettings: View {
|
||||
List {
|
||||
sections
|
||||
}
|
||||
#if os(tvOS)
|
||||
.listStyle(.plain)
|
||||
#elseif os(iOS)
|
||||
.listStyle(.insetGrouped)
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
.sheet(isPresented: $presentingProfileForm) {
|
||||
QualityProfileForm(qualityProfileID: $editedProfileID)
|
||||
}
|
||||
#if os(tvOS)
|
||||
.buttonStyle(.plain)
|
||||
.toggleStyle(TVOSPlainToggleStyle())
|
||||
.frame(maxWidth: 1000)
|
||||
#elseif os(iOS)
|
||||
.listStyle(.insetGrouped)
|
||||
|
||||
@@ -164,6 +164,7 @@ struct SettingsView: View {
|
||||
Text("Not Selected")
|
||||
}
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
Divider()
|
||||
}
|
||||
@@ -175,30 +176,45 @@ struct SettingsView: View {
|
||||
} label: {
|
||||
Label("Browsing", systemImage: "list.and.film").labelStyle(SettingsLabel())
|
||||
}
|
||||
#if os(tvOS)
|
||||
.buttonStyle(.plain)
|
||||
#endif
|
||||
|
||||
NavigationLink {
|
||||
PlayerSettings()
|
||||
} label: {
|
||||
Label("Player", systemImage: "play.rectangle").labelStyle(SettingsLabel())
|
||||
}
|
||||
#if os(tvOS)
|
||||
.buttonStyle(.plain)
|
||||
#endif
|
||||
|
||||
NavigationLink {
|
||||
PlayerControlsSettings()
|
||||
} label: {
|
||||
Label("Controls", systemImage: "hand.tap").labelStyle(SettingsLabel())
|
||||
}
|
||||
#if os(tvOS)
|
||||
.buttonStyle(.plain)
|
||||
#endif
|
||||
|
||||
NavigationLink {
|
||||
QualitySettings()
|
||||
} label: {
|
||||
Label("Quality", systemImage: "4k.tv").labelStyle(SettingsLabel())
|
||||
}
|
||||
#if os(tvOS)
|
||||
.buttonStyle(.plain)
|
||||
#endif
|
||||
|
||||
NavigationLink {
|
||||
HistorySettings()
|
||||
} label: {
|
||||
Label("History", systemImage: "clock.arrow.circlepath").labelStyle(SettingsLabel())
|
||||
}
|
||||
#if os(tvOS)
|
||||
.buttonStyle(.plain)
|
||||
#endif
|
||||
|
||||
if !accounts.isEmpty {
|
||||
NavigationLink {
|
||||
@@ -206,6 +222,9 @@ struct SettingsView: View {
|
||||
} label: {
|
||||
Label("SponsorBlock", systemImage: "dollarsign.circle").labelStyle(SettingsLabel())
|
||||
}
|
||||
#if os(tvOS)
|
||||
.buttonStyle(.plain)
|
||||
#endif
|
||||
}
|
||||
|
||||
NavigationLink {
|
||||
@@ -213,12 +232,18 @@ struct SettingsView: View {
|
||||
} label: {
|
||||
Label("Locations", systemImage: "globe").labelStyle(SettingsLabel())
|
||||
}
|
||||
#if os(tvOS)
|
||||
.buttonStyle(.plain)
|
||||
#endif
|
||||
|
||||
NavigationLink {
|
||||
AdvancedSettings()
|
||||
} label: {
|
||||
Label("Advanced", systemImage: "wrench.and.screwdriver").labelStyle(SettingsLabel())
|
||||
}
|
||||
#if os(tvOS)
|
||||
.buttonStyle(.plain)
|
||||
#endif
|
||||
}
|
||||
#if os(tvOS)
|
||||
.padding(.horizontal, 20)
|
||||
@@ -232,6 +257,9 @@ struct SettingsView: View {
|
||||
} label: {
|
||||
Label("Help", systemImage: "questionmark.circle").labelStyle(SettingsLabel())
|
||||
}
|
||||
#if os(tvOS)
|
||||
.buttonStyle(.plain)
|
||||
#endif
|
||||
}
|
||||
#if os(tvOS)
|
||||
.padding(.horizontal, 20)
|
||||
@@ -287,6 +315,7 @@ struct SettingsView: View {
|
||||
Label("Import Settings", systemImage: "square.and.arrow.down")
|
||||
.labelStyle(SettingsLabel())
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.padding(.horizontal, 20)
|
||||
#else
|
||||
Button(action: importSettings) {
|
||||
|
||||
@@ -23,9 +23,14 @@ struct SponsorBlockSettings: View {
|
||||
List {
|
||||
sections
|
||||
}
|
||||
#if os(tvOS)
|
||||
.listStyle(.plain)
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#if os(tvOS)
|
||||
.buttonStyle(.plain)
|
||||
.toggleStyle(TVOSPlainToggleStyle())
|
||||
.frame(maxWidth: 1000)
|
||||
#else
|
||||
.frame(minWidth: 0, maxWidth: .infinity, alignment: .leading)
|
||||
|
||||
17
Shared/Settings/TVOSPlainToggleStyle.swift
Normal file
17
Shared/Settings/TVOSPlainToggleStyle.swift
Normal file
@@ -0,0 +1,17 @@
|
||||
import SwiftUI
|
||||
|
||||
#if os(tvOS)
|
||||
struct TVOSPlainToggleStyle: ToggleStyle {
|
||||
func makeBody(configuration: Configuration) -> some View {
|
||||
Button(action: { configuration.isOn.toggle() }) {
|
||||
HStack {
|
||||
configuration.label
|
||||
Spacer()
|
||||
Image(systemName: configuration.isOn ? "checkmark.circle.fill" : "circle")
|
||||
.foregroundColor(configuration.isOn ? .accentColor : .secondary)
|
||||
}
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -3,14 +3,28 @@ import SwiftUI
|
||||
|
||||
struct SettingsPickerModifier: ViewModifier {
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
#if os(tvOS)
|
||||
.pickerStyle(.inline)
|
||||
#endif
|
||||
#if os(iOS)
|
||||
.pickerStyle(.automatic)
|
||||
content
|
||||
.pickerStyle(.inline)
|
||||
.onAppear {
|
||||
// Force refresh to apply button style to picker options
|
||||
}
|
||||
#elseif os(iOS)
|
||||
content
|
||||
.pickerStyle(.automatic)
|
||||
#else
|
||||
.labelsHidden()
|
||||
content
|
||||
.labelsHidden()
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if os(tvOS)
|
||||
// Extension to help remove picker row backgrounds
|
||||
extension View {
|
||||
func pickerRowStyle() -> some View {
|
||||
self.buttonStyle(.plain)
|
||||
.listRowBackground(Color.clear)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user