From c7b901b10397b127c9e02d299a4e837f9a29a23d Mon Sep 17 00:00:00 2001 From: Scarriffle Date: Wed, 30 Sep 2026 15:59:04 +0200 Subject: [PATCH] =?UTF-8?q?Der=20Player=20sagt=20jetzt,=20was=20er=20?= =?UTF-8?q?=C3=BCber=20seinen=20Platz=20weiss?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Zweimal habe ich behauptet, die Ränder seien behoben, und zweimal sassen die Knöpfe weiter ausserhalb. Im Simulator steht dort „SA 0,62,62,20 · Schirm 874×402" und alles liegt drin — nachstellen lässt es sich hier also nicht. Damit ist Raten der falsche Weg. **Eine Zeile im Entwicklerbau**, unter dem Schieberegler: die Ränder, wie sie ankommen, die Bildschirmgrösse daneben und der Skalierungsmodus. Sitzen die Knöpfe daneben, gibt es genau drei Möglichkeiten — die Ränder kommen als null an, sie kommen falsch an, oder sie stimmen und der Rahmen ist grösser als der Schirm. Von aussen sehen alle drei gleich aus. Die Zeile unterscheidet sie. **Und eine Härtung, die beim Nachlesen auffiel.** `ScreenSafeArea` suchte nur die Szene, die `foregroundActive` ist. Genau die gibt es in zwei Momenten nicht: während einer Drehung und kurz nach dem Zurückholen der App. Wer dann fragt, bekommt `nil`, daraus werden Ränder von null — und die bleiben stehen, bis sich die Fläche wieder ändert. Die Knöpfe sitzen dann dauerhaft im Home-Indikator, obwohl die Rechnung stimmt. Ein Fenster mit den richtigen Rändern ist in dem Moment trotzdem da, es ist nur nicht „aktiv". Jetzt der Reihe nach: aktiv, sonst sichtbar, sonst irgendeines. Dazu wird beim Zurückholen der App nachgefasst — eine Drehung im Hintergrund ändert die Fläche, ohne dass die Ansicht davon etwas merkt. Ob das der Fehler war, weiss ich nicht. Die Zeile wird es sagen. Co-Authored-By: Claude Opus 5 --- Flixy/Views/Player/PlayerView.swift | 33 ++++++++++++++++++++++ Flixy/Views/Player/ScreenSafeArea.swift | 37 +++++++++++++++++++------ 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/Flixy/Views/Player/PlayerView.swift b/Flixy/Views/Player/PlayerView.swift index 36211ae..e00f1b9 100644 --- a/Flixy/Views/Player/PlayerView.swift +++ b/Flixy/Views/Player/PlayerView.swift @@ -242,6 +242,17 @@ struct PlayerView: View { safeArea = ScreenSafeArea.current } .onAppear { safeArea = ScreenSafeArea.current } + // **Auch beim Zurückholen der App.** Eine Drehung im Hintergrund + // ändert die Fläche, ohne dass `onGeometryChange` hier feuert — die + // Ansicht ist dann gar nicht sichtbar. Wer die App danach quer + // wiederfindet, hätte sonst die Ränder von vorhin. + .onReceive( + NotificationCenter.default.publisher( + for: UIApplication.didBecomeActiveNotification + ) + ) { _ in + safeArea = ScreenSafeArea.current + } .animation(.easeInOut(duration: 0.2), value: model.isBuffering) // **Herein sanft, hinaus zügig.** Das Einblenden begleitet eine // Bewegung und soll der Hand entgegenkommen; das Ausblenden ist ein @@ -353,6 +364,7 @@ struct PlayerView: View { transport(for: model) Spacer() scrubber(for: model) + diagnose(for: model) } .padding(FlixySpacing.loose) // **Zusätzlich zum Polster, nicht statt.** Der Sicherheitsrand sagt, @@ -377,6 +389,27 @@ struct PlayerView: View { } } + /// Was der Player über seinen Platz weiss. + /// + /// **Nur im Entwicklerbau, und nur, weil zweimal Raten nicht gereicht + /// hat.** Sitzen die Knöpfe ausserhalb des Bildschirms, gibt es genau + /// drei Möglichkeiten: die Ränder kommen als null an, sie kommen falsch + /// an, oder sie stimmen und der Rahmen ist grösser als der Schirm. Von + /// aussen sehen alle drei gleich aus. Diese Zeile unterscheidet sie. + /// + /// Die Schirmgrösse daneben ist dabei der Schlüssel: steht dort etwas + /// anderes als die Fläche, auf der die Knöpfe liegen, liegt es nicht an + /// den Rändern. + @ViewBuilder + private func diagnose(for model: PlayerModel) -> some View { + #if DEBUG + let schirm = ScreenSafeArea.screenSize + Text(verbatim: "SA \(Int(safeArea.top)),\(Int(safeArea.leading)),\(Int(safeArea.trailing)),\(Int(safeArea.bottom)) Schirm \(Int(schirm.width))×\(Int(schirm.height)) \(model.videoFit == .fill ? "füllen" : "einpassen")") + .font(.system(size: 11).monospaced()) + .foregroundStyle(.yellow) + #endif + } + private func topBar(for model: PlayerModel) -> some View { HStack(alignment: .top, spacing: FlixySpacing.regular) { // Der Pfeil verkleinert, er beendet nicht. Das ist der ganze diff --git a/Flixy/Views/Player/ScreenSafeArea.swift b/Flixy/Views/Player/ScreenSafeArea.swift index 54340f1..5801031 100644 --- a/Flixy/Views/Player/ScreenSafeArea.swift +++ b/Flixy/Views/Player/ScreenSafeArea.swift @@ -38,9 +38,7 @@ enum ScreenSafeArea { // Bildschirmseiten, SwiftUI in Leserichtung. Bei einer Sprache, die // von rechts nach links läuft, wären sie vertauscht — deshalb hier // ausdrücklich in die Leserichtung übersetzt. - let vonLinks = UIApplication.shared.connectedScenes - .compactMap { $0 as? UIWindowScene } - .first?.keyWindow?.effectiveUserInterfaceLayoutDirection != .rightToLeft + let vonLinks = fenster?.effectiveUserInterfaceLayoutDirection != .rightToLeft return EdgeInsets( top: i.top, leading: vonLinks ? i.left : i.right, @@ -49,10 +47,33 @@ enum ScreenSafeArea { ) } - private static var windowInsets: UIEdgeInsets { - UIApplication.shared.connectedScenes - .compactMap { $0 as? UIWindowScene } - .first { $0.activationState == .foregroundActive }? - .keyWindow?.safeAreaInsets ?? .zero + /// Wie gross der Bildschirm gerade ist. + /// + /// Nur für die Diagnose: sie beantwortet die Frage, ob der Player grösser + /// gezeichnet wird als der Schirm — und die lässt sich von innen sonst + /// nicht stellen. + static var screenSize: CGSize { fenster?.bounds.size ?? .zero } + + private static var windowInsets: UIEdgeInsets { fenster?.safeAreaInsets ?? .zero } + + /// Das Fenster, das gerade zählt. + /// + /// **Mit Rückfall, und das ist der Punkt.** Zuerst stand hier nur + /// „die Szene, die `foregroundActive` ist" — und genau die gibt es in + /// zwei Momenten nicht: während einer Drehung und kurz nach dem + /// Zurückholen der App. Wer dann fragt, bekommt `nil`, daraus werden + /// Ränder von null, und die bleiben stehen, bis sich die Fläche wieder + /// ändert. Die Knöpfe sitzen dann im Home-Indikator, und zwar dauerhaft, + /// obwohl die Rechnung stimmt. + /// + /// Ein Fenster, das die richtigen Ränder kennt, ist in dem Moment + /// trotzdem da — es ist nur nicht „aktiv". Also der Reihe nach: aktiv, + /// sonst sichtbar, sonst irgendeines. + private static var fenster: UIWindow? { + let szenen = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene } + let gewählt = szenen.first { $0.activationState == .foregroundActive } + ?? szenen.first { $0.activationState == .foregroundInactive } + ?? szenen.first + return gewählt?.keyWindow ?? gewählt?.windows.first { $0.isKeyWindow } ?? gewählt?.windows.first } }