Die Bibliothek beginnt jetzt in einer Mediathek
Vorher stand dort eine Liste aller Mediatheken: auf „Bibliothek" tippen, dann auf „Filme", dann endlich ein Raster. In eine andere wechseln hiess zurück und wieder hinein. Der Zwischenbildschirm hat nie etwas erzählt, was eine Leiste nicht auch zeigen kann — er war ein Platzproblem, kein Entwurf. Jetzt steht man sofort in einer Mediathek, und darüber der unteren Leiste liegt eine zweite mit den Mediatheken und Live-TV. Auf dem iPad und am Mac ändert sich nichts: dort steht jede Mediathek einzeln in der Seitenleiste, auf dem Fernseher nebeneinander in der oberen Leiste — es ist dieselbe Idee in der Form, die ein Telefon hergibt. **Warum eine zweite Leiste und nicht Plätze in der unteren.** Die hat fünf, und alles darüber schiebt das System in ein „Mehr"-Verzeichnis, dessen Einträge zwei Navigationsleisten übereinander tragen. Genau daran ist der Zwischenbildschirm seinerzeit entstanden. Eine selbst gezeichnete Leiste kennt diese Grenze nicht — und sie sieht aus wie die Insel auf dem iPad, weil sie dieselbe Fläche benutzt: `FlixySidebar.surface(in:)` ist ein Bauteil, kein nachgebautes Aussehen, und wird jetzt zum dritten Mal gerufen. Als Sicherheitsrand und nicht als Überzug, damit die Liste über der Leiste endet statt dahinter zu verschwinden. Bei mehr als vier Mediatheken wird sie waagerecht scrollbar. **Das Monogramm steht jetzt in der Werkzeugleiste**, am rechten Ende, auf derselben Höhe wie Suche, Filter und Ansicht — die rücken dafür nach links. Als Überzug lag es über dem Inhalt, auf einem Raster also über einem Plakat. Dass es dabei an den Anfang der Reihe rutschte, hat einen Grund, der im Quelltext steht: SwiftUI reiht Werkzeugleisten von aussen nach innen, ein von aussen angehängter Eintrag steht deshalb **vor** den übrigen. Wo in der Reihe etwas steht, kann nur der entscheiden, der die Reihe schreibt. Auf der Startseite bleibt es ein Überzug — dort gibt es keine Navigationsleiste. Dazu: die Zuordnung Mediathekenart → Symbol stand dreimal im Projekt, eine davon falsch (jede Mediathek bekam „film", auch eine mit Serien). Jetzt einmal, neben den übrigen Ableitungen aus den Modellen. Geprüft im Simulator: „Bibliothek" öffnet sofort das Raster, die Leiste wechselt zwischen Filme, Serien, Gemischtes und Live-TV, und das Monogramm steht ganz rechts in der Werkzeugleiste. iOS und tvOS bauen, 469 Tests grün. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
0aa11e1c25
commit
7f380e5f4d
@@ -30,3 +30,32 @@ struct AccountAvatar: View {
|
||||
.background(accent, in: .circle)
|
||||
}
|
||||
}
|
||||
|
||||
extension EnvironmentValues {
|
||||
/// Öffnet die Einstellungen.
|
||||
///
|
||||
/// **Über die Umgebung, weil der Knopf an mehreren Stellen steht.** Das
|
||||
/// Blatt gehört der Wurzel — nur sie überlebt den Wechsel des Bereichs.
|
||||
/// Jeder Bildschirm, der den Knopf zeigt, müsste den Weg dorthin sonst
|
||||
/// als Argument durchgereicht bekommen.
|
||||
@Entry var flixyOpenSettings: () -> Void = {}
|
||||
}
|
||||
|
||||
/// Das Monogramm als Knopf für eine Werkzeugleiste.
|
||||
///
|
||||
/// **Als Ansicht und nicht als Modifikator.** Ein Modifikator hängt seine
|
||||
/// Einträge aussen an, und SwiftUI reiht aussen zuerst — das Monogramm stand
|
||||
/// damit **vor** Suche, Filter und Ansicht statt dahinter. Wo in der Reihe es
|
||||
/// steht, kann nur der entscheiden, der die Reihe schreibt.
|
||||
struct AccountToolbarButton: View {
|
||||
@Environment(FlixySession.self) private var session
|
||||
@Environment(\.flixyOpenSettings) private var openSettings
|
||||
|
||||
var body: some View {
|
||||
Button(action: openSettings) {
|
||||
AccountAvatar(name: session.accountName ?? session.serverName, diameter: 34)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.accessibilityLabel(Text("sidebar.settings"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,6 +275,14 @@ struct RootView: View {
|
||||
TabView(selection: $selection) {
|
||||
Tab("sidebar.home", systemImage: "house", value: SidebarDestination.home) {
|
||||
mediaStack { HomeView() }
|
||||
// **Nur hier.** Die Startseite hat keine
|
||||
// Navigationsleiste — sie war leer und belegte trotzdem
|
||||
// Höhe —, also ist ein Überzug der einzige Platz. Die
|
||||
// übrigen Bildschirme haben eine, und dort stünde er
|
||||
// entweder über deren Knöpfen oder mitten im Inhalt. Wer
|
||||
// zu den Einstellungen will, geht auf Start; das ist ein
|
||||
// Schritt und kein Umweg.
|
||||
.overlay(alignment: .topTrailing) { profileButton }
|
||||
}
|
||||
.customizationID("flixy.tab.home")
|
||||
|
||||
@@ -292,7 +300,7 @@ struct RootView: View {
|
||||
) {
|
||||
mediaStack { LibraryView(library: library) }
|
||||
} label: {
|
||||
sidebarLabel(Text(library.title), symbol: symbol(for: library.kind))
|
||||
sidebarLabel(Text(library.title), symbol: library.kind.symbol)
|
||||
}
|
||||
.customizationID("flixy.tab.library.\(library.id)")
|
||||
}
|
||||
@@ -311,7 +319,7 @@ struct RootView: View {
|
||||
}
|
||||
} else {
|
||||
Tab(value: SidebarDestination.libraryHub) {
|
||||
mediaStack { LibraryHubView() }
|
||||
mediaStack { LibraryBrowser() }
|
||||
} label: {
|
||||
tabLabel("sidebar.library", symbol: "books.vertical")
|
||||
}
|
||||
@@ -449,7 +457,7 @@ struct RootView: View {
|
||||
// Insel erreichen: Inhalt kam dort nie hin. Jetzt kommt er.
|
||||
.ignoresSafeArea(.container, edges: .top)
|
||||
} else {
|
||||
tabs.overlay(alignment: .topTrailing) { profileButton }
|
||||
tabs
|
||||
}
|
||||
#else
|
||||
tabs
|
||||
@@ -460,6 +468,7 @@ struct RootView: View {
|
||||
// Navigationsstapel um sich — die Medienziele aus `mediaStack` ruft
|
||||
// es nie auf. Und beim Schliessen steht man wieder dort, wo man war,
|
||||
// statt in einem Bereich, den man erst wieder verlassen muss.
|
||||
.environment(\.flixyOpenSettings, { showsSettings = true })
|
||||
.sheet(isPresented: $showsSettings) {
|
||||
NavigationStack {
|
||||
SettingsView()
|
||||
@@ -500,14 +509,17 @@ struct RootView: View {
|
||||
Button {
|
||||
showsSettings = true
|
||||
} label: {
|
||||
AccountAvatar(name: session.accountName ?? session.serverName, diameter: 32)
|
||||
AccountAvatar(name: session.accountName ?? session.serverName, diameter: 34)
|
||||
.padding(FlixySpacing.snug)
|
||||
// Damit er auch über einem hellen Aufmacher steht.
|
||||
.background(.black.opacity(0.28), in: .circle)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
.padding(.trailing, FlixySpacing.regular)
|
||||
.padding(.top, statusBarHeight + FlixySpacing.snug)
|
||||
// **Ohne die Statusleiste.** Auf dem iPhone sitzt der Baum innerhalb
|
||||
// des Sicherheitsrands; sie noch einmal dazuzurechnen schöbe den
|
||||
// Knopf um ihre Höhe nach unten, mitten ins Bild.
|
||||
.padding(.top, FlixySpacing.snug)
|
||||
.opacity(topChrome.isRevealed ? 1 : 0)
|
||||
.allowsHitTesting(topChrome.isRevealed)
|
||||
.animation(.snappy(duration: 0.28), value: topChrome.isRevealed)
|
||||
@@ -543,7 +555,7 @@ struct RootView: View {
|
||||
NavigationEntry(
|
||||
destination: .library(key: library.id, title: library.title),
|
||||
title: Text(library.title),
|
||||
symbol: symbol(for: library.kind),
|
||||
symbol: library.kind.symbol,
|
||||
plainTitle: library.title
|
||||
)
|
||||
}
|
||||
@@ -631,11 +643,4 @@ struct RootView: View {
|
||||
#endif
|
||||
}
|
||||
|
||||
private func symbol(for kind: MediaLibrary.Kind) -> String {
|
||||
switch kind {
|
||||
case .movie: "film"
|
||||
case .show: "tv"
|
||||
default: "rectangle.stack"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user