Files
Flaro/Flixy/Views/Player/ScreenSafeArea.swift
T
ScarriffleandClaude Opus 5 22f824b000 Die Ränder kommen jetzt von der Stelle, die sie kennt
Mein voriger Versuch war an der Quelle falsch, nicht an der Rechnung.

**Was ich gebaut hatte:** die Sicherheitsränder in `RootView` messen und
über die Umgebung an den Player reichen. Gemessen in einer Ansicht,
angewendet in einer anderen, über eine Drehung hinweg — drei
Gelegenheiten, daneben zu liegen. Im Simulator ging es gut, auf dem
Gerät nicht: dort standen im Querformat links 75 und rechts 28 Punkt
Rand, und der Untertitel wurde von der Bildschirmkante abgeschnitten.

**Warum SwiftUI hier nicht hilft.** Der Sicherheitsrand ist dort kein
Wert, den man erfragt, sondern einer, den man verbraucht: wer ihn
respektiert, sieht null, und wer ihn mit `ignoresSafeArea` überschreitet,
sieht danach ebenfalls null. Beide Auswege habe ich durchgespielt und
beide gemessen. Nimmt man der Ansicht das `ignoresSafeArea`, sitzen die
Knöpfe richtig — aber die Bildfläche verliert es mit, und das Bild ist im
Querformat um die Notch-Breite schmaler. Schiebt man einen
`GeometryReader` dazwischen, passiert dasselbe.

**Das Fenster weiß es immer**, in der richtigen Ausrichtung und in dem
Moment, in dem gefragt wird. `ScreenSafeArea` ist die eine Stelle in
dieser App, die so fragt, und die eine, an der es keinen SwiftUI-Weg
gibt. Nachgeholt wird der Wert bei jeder Änderung der Fläche — das ist
genau der Moment des Drehens.

Nachgemessen im Simulator gegen die Jellyfin-Attrappe: im Querformat
meldet das Fenster 0/62/62/20, und damit stehen Kopfzeile, Schieberegler
und Zeitangaben vollständig im sichtbaren Bereich, während das Bild bis
an die Kanten reicht.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-29 17:32:50 +02:00

59 lines
2.6 KiB
Swift

import SwiftUI
import UIKit
/// Wo der Bildschirm aufhört — für die Ansichten, die absichtlich darüber
/// hinauslaufen.
///
/// **Warum nicht aus SwiftUI.** Der Sicherheitsrand ist in SwiftUI kein Wert,
/// den man erfragt, sondern ein Rand, den man verbraucht: wer ihn respektiert,
/// sieht ihn als null, und wer ihn mit `ignoresSafeArea` überschreitet, sieht
/// ihn danach ebenfalls als null. Ein `GeometryReader` dazwischenzuschieben
/// hilft nicht, er nimmt der Bildfläche das `ignoresSafeArea` gleich mit — und
/// dann ist das Bild seitlich um die Notch-Breite kleiner.
///
/// Der Versuch, ihn eine Ebene höher zu messen und hineinzureichen, hat es
/// auch nicht behoben: gemessen in einer Ansicht, angewendet in einer anderen,
/// über eine Drehung hinweg. Auf dem Gerät kamen dabei die Werte der falschen
/// Ausrichtung an.
///
/// **Das Fenster weiß es immer.** Es kennt seine Ausrichtung, es kennt die
/// Notch, und es antwortet in dem Moment, in dem gefragt wird. Das ist die
/// einzige Stelle in dieser App, die so fragt — und genau die eine, an der es
/// keinen SwiftUI-Weg gibt.
///
/// Gebraucht wird es für die Untertitel im Füllmodus: die Bildebene reicht
/// dort über die untere Kante hinaus, und AVFoundation zeichnet die Schrift an
/// ihren unteren Rand.
@MainActor
enum ScreenSafeArea {
/// Was der Bildschirm gerade verdeckt — Notch, Dynamic Island,
/// Home-Indikator, gerundete Ecken.
///
/// **Jedes Mal frisch gefragt, nie gemerkt.** Beim Drehen ändert sich
/// jeder dieser Werte, und ein gemerkter wäre genau dann falsch, wenn er
/// gebraucht wird.
static var current: EdgeInsets {
let i = windowInsets
// `left`/`right` und nicht `leading`/`trailing`: UIKit denkt in
// 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
return EdgeInsets(
top: i.top,
leading: vonLinks ? i.left : i.right,
bottom: i.bottom,
trailing: vonLinks ? i.right : i.left
)
}
private static var windowInsets: UIEdgeInsets {
UIApplication.shared.connectedScenes
.compactMap { $0 as? UIWindowScene }
.first { $0.activationState == .foregroundActive }?
.keyWindow?.safeAreaInsets ?? .zero
}
}