Die Einstellungen lagen hinter der Leiste
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 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
ab0adef8d4
commit
9a5412dc5d
@@ -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
|
||||
|
||||
@@ -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<Content: View>(@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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user