From 9a5412dc5dacd7f9b6f9ead3f4518d2badc128bb Mon Sep 17 00:00:00 2001 From: Scarriffle Date: Sun, 13 Sep 2026 20:44:20 +0200 Subject: [PATCH] Die Einstellungen lagen hinter der Leiste MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ein Sicherheitsrand je Bildschirm wandert nicht in einen geschobenen Bildschirm mit. Die Einstellungen schieben ihre Unterseiten über gewöhnliche Verweise, und dort fehlte er: die Beschriftungen lagen hinter der Seitenleiste und wurden von der Maske abgeschnitten — zu sehen waren nur die rechtsbündigen Werte. Der Platz für die Leiste ist jetzt ein echter Rand am ganzen TabView. Der gilt für alles, was darin aufgeht. Dazu: der Aufmacher nimmt zweiundachtzig statt hundert Prozent der Höhe. Über die ganze Höhe sah er aus wie die einzige Seite der App — von den Reihen war ohne Scrollen nichts zu sehen. So lugt die erste gerade herein. Und das Umschalten zwischen Seite und oben hat eine Bewegung: die Leiste kommt von der Seite beziehungsweise von oben herein und geht dorthin zurück. Die Überlagerung behält dabei ihre Ausrichtung — wechselte sie mit, sprang die Leiste im Übergang an die neue Ecke, statt dorthin zu wandern. Co-Authored-By: Claude Opus 5 --- Flixy/Views/Home/HeroCarousel.swift | 8 ++++-- Flixy/Views/Navigation/RootView.swift | 41 ++++++++++++++++++--------- FlixyUITests/LayoutSnapshots.swift | 28 ++++++++++++++++++ 3 files changed, 61 insertions(+), 16 deletions(-) diff --git a/Flixy/Views/Home/HeroCarousel.swift b/Flixy/Views/Home/HeroCarousel.swift index 7284a97..5156895 100644 --- a/Flixy/Views/Home/HeroCarousel.swift +++ b/Flixy/Views/Home/HeroCarousel.swift @@ -88,12 +88,14 @@ struct HeroCarousel: View { // Die Höhe: fast das ganze Fenster. // // Ein Aufmacher, der nur ein Drittel hoch ist, sieht aus wie ein - // breites Vorschaubild. So hoch wie die Seitenleiste soll er sein, - // und die ist so hoch wie das Fenster. + // breites Vorschaubild — einer über die ganze Höhe dagegen wie die + // einzige Seite der App: von den Reihen war nichts mehr zu sehen, + // ohne zu scrollen. Zweiundachtzig Prozent lassen die erste Reihe + // gerade hereinlugen, und damit weiß man, dass es weitergeht. // // Auf einem schmalen Gerät nicht: dort ist der Aufmacher hochkant // und rechnet sich seine Höhe aus der Breite aus. - .modifier(HeroHeight(fraction: sizeClass == .compact ? nil : 1)) + .modifier(HeroHeight(fraction: sizeClass == .compact ? nil : 0.82)) // **Kein** Seitenverhältnis hier: der Aufmacher bringt sein // eigenes mit, und eines darüber legte ihn auf die Breite des // Inhaltsbereichs fest. Weil er nach links unter die Seitenleiste diff --git a/Flixy/Views/Navigation/RootView.swift b/Flixy/Views/Navigation/RootView.swift index 3d9916f..d2f0400 100644 --- a/Flixy/Views/Navigation/RootView.swift +++ b/Flixy/Views/Navigation/RootView.swift @@ -285,9 +285,22 @@ struct RootView: View { #if os(iOS) if sizeClass == .regular { tabs + // Der Platz für die Leiste: als **echter** Rand am ganzen + // TabView, nicht als Sicherheitsrand je Bildschirm. + // + // Ein Sicherheitsrand wandert nicht in einen geschobenen + // Bildschirm mit. Die Einstellungen schieben ihre + // Unterseiten über gewöhnliche Verweise, und dort fehlte + // er: die Beschriftungen lagen hinter der Leiste und waren + // von der Maske abgeschnitten. Ein Rand am TabView gilt + // für alles, was darin aufgeht. + .padding(chromeInsets) .background { TabChromeSuppressor().frame(width: 0, height: 0) } .mask(alignment: .topLeading) { chromeMask } - .overlay(alignment: placement == .sidebar ? .topLeading : .top) { + // Immer dieselbe Ausrichtung: wechselte sie mit der + // Stellung, sprang die Leiste im Übergang an die neue Ecke, + // statt dorthin zu wandern. + .overlay(alignment: .topLeading) { chrome } } else { @@ -310,6 +323,11 @@ struct RootView: View { placement: $placement, layout: navigationLayout ) + // Sie kommt von der Seite herein und geht dorthin zurück. Ohne das + // erschien sie schlagartig an ihrem Platz, und beim Umschalten war + // nicht zu sehen, dass dasselbe umgezogen ist. + .transition(.move(edge: .leading).combined(with: .opacity)) + case .top: // In der Mitte, und zwar erzwungen: die Leiste ist so breit wie // ihr Inhalt, und eine Überlagerung richtet sie dann nicht @@ -323,6 +341,7 @@ struct RootView: View { ) Spacer(minLength: 0) } + .transition(.move(edge: .top).combined(with: .opacity)) } } @@ -462,22 +481,18 @@ struct RootView: View { /// ganze Witz: eine Reihe bleibt damit fensterbreit und schiebt ihre /// Kacheln beim Wischen hinter die Leiste, stellt aber im Ruhezustand /// keine dorthin. Mit einem gewöhnlichen Abstand endete sie an der Kante. + /// Sagt einem Bildschirm, wie weit er nach links ausbrechen darf. + /// + /// Nur noch das: den Platz nimmt sich der Rand am TabView. Hier bleibt die + /// Angabe für den Aufmacher, der bewusst darüber hinausgreift. @ViewBuilder private func chromed(@ViewBuilder content: () -> Content) -> some View { #if os(iOS) - let insets = sizeClass == .regular ? chromeInsets : EdgeInsets() content() - .safeAreaPadding(insets) - // Wie weit der Aufmacher nach links ausbrechen darf: bis genau an - // die **Kante der Leiste**, nicht bis an den Fensterrand. - // - // Der Unterschied ist ein Streifen von zwölf Punkten, und der war - // deutlich zu sehen: die Leiste schwebt mit Abstand zum Rand, das - // Bild lief daran vorbei, und neben der Leiste stand ein heller - // Streifen Bild, den nichts abdeckte. Endet das Bild an ihrer - // Kante, sieht man es nur noch **durch** sie hindurch — und genau - // so soll es sein. - .environment(\.flixyChromeInset, max(insets.leading - FlixySidebar.margin, 0)) + .environment( + \.flixyChromeInset, + sizeClass == .regular ? max(chromeInsets.leading - FlixySidebar.margin, 0) : 0 + ) #else content() #endif diff --git a/FlixyUITests/LayoutSnapshots.swift b/FlixyUITests/LayoutSnapshots.swift index a256ce4..419eb05 100644 --- a/FlixyUITests/LayoutSnapshots.swift +++ b/FlixyUITests/LayoutSnapshots.swift @@ -34,6 +34,34 @@ final class LayoutSnapshots: XCTestCase { /// Beim Umschalten steht die Leiste kurz falsch, bevor sie sich fängt. /// Fotografiert wird in Serie, weil genau der Zwischenzustand gesucht ist /// und ein einzelnes Bild danach nichts zeigt. + /// Eine Unterseite der Einstellungen. + /// + /// Sie wird über einen gewöhnlichen Verweis geschoben und nicht über eine + /// angemeldete Route — genau dort fehlte der Platz für die Seitenleiste, + /// und die Beschriftungen lagen hinter ihr. + func testPushedSettingsScreen() throws { + let app = XCUIApplication() + app.launchArguments = [ + "-FlixyStubServer", "http://127.0.0.1:8096", + "-flixy.navigation.placement", "sidebar", + ] + app.launch() + + XCUIDevice.shared.orientation = .landscapeLeft + XCTAssertTrue(app.staticTexts["Weiterschauen"].waitForExistence(timeout: 20)) + + app.buttons["Einstellungen"].firstMatch.tap() + Thread.sleep(forTimeInterval: 2) + shoot(app, "30-einstellungen") + + let server = app.buttons["Server"].firstMatch + if server.waitForExistence(timeout: 5) { + server.tap() + Thread.sleep(forTimeInterval: 2) + shoot(app, "31-server") + } + } + func testSidebarToggle() throws { let app = XCUIApplication() // Die Stellung der Leiste wird gespeichert, und ein voriger Lauf hat