diff --git a/Flixy/Views/Player/PlayerView.swift b/Flixy/Views/Player/PlayerView.swift index e00f1b9..153f69e 100644 --- a/Flixy/Views/Player/PlayerView.swift +++ b/Flixy/Views/Player/PlayerView.swift @@ -49,6 +49,16 @@ struct PlayerView: View { /// genau der Moment des Drehens, und genau dann stimmen die alten Werte /// nicht mehr. @State private var safeArea = EdgeInsets() + #if DEBUG + /// Wie gross der Player gezeichnet wird und wo er anfängt. + /// + /// **Die Frage, an der alles hängt.** Ist der Rahmen grösser als der + /// Bildschirm, liegt es nicht an den Rändern — dann wird der Player in + /// einen zu grossen Kasten gelegt und mittig darin ausgerichtet, und + /// oben wie unten fällt gleich viel heraus. Genau so sieht es aus, wenn + /// die Kopfzeile fehlt und die Zeitangaben auch. + @State private var rahmen: CGRect = .zero + #endif @State private var showsControls = true @State private var trackPicker: TrackPickerSheet.Kind? @State private var showsQualityChoice = false @@ -241,6 +251,11 @@ struct PlayerView: View { } action: { _ in safeArea = ScreenSafeArea.current } + #if DEBUG + .onGeometryChange(for: CGRect.self) { proxy in + proxy.frame(in: .global) + } action: { rahmen = $0 } + #endif .onAppear { safeArea = ScreenSafeArea.current } // **Auch beim Zurückholen der App.** Eine Drehung im Hintergrund // ändert die Fläche, ohne dass `onGeometryChange` hier feuert — die @@ -404,7 +419,7 @@ struct PlayerView: View { 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")") + Text(verbatim: "SA \(Int(safeArea.top)),\(Int(safeArea.leading)),\(Int(safeArea.trailing)),\(Int(safeArea.bottom)) Rahmen \(Int(rahmen.width))×\(Int(rahmen.height)) @\(Int(rahmen.minX)),\(Int(rahmen.minY)) Schirm \(Int(schirm.width))×\(Int(schirm.height)) \(model.videoFit == .fill ? "füllen" : "einpassen")") .font(.system(size: 11).monospaced()) .foregroundStyle(.yellow) #endif @@ -867,9 +882,9 @@ struct PlayerView: View { engine: model.engine, surface: model.surface, isActive: playback.presentation == .full, - // **Nur im Füllmodus.** Eingepasst liegen unten ohnehin schwarze - // Balken, in denen der Untertitel Platz hat; dort etwas - // auszusparen nähme Bild, ohne etwas zu retten. + // **Nur im Füllmodus.** Eingepasst liegt das Bild seit + // Neuestem ohnehin im sicheren Bereich — dort kann kein + // Untertitel mehr unter die Kante geraten. bottomInset: model.videoFit == .fill ? safeArea.bottom : 0 ) @@ -882,7 +897,24 @@ struct PlayerView: View { } else if let breite = model.statistics?.width, let höhe = model.statistics?.height, breite > 0, höhe > 0 { - fläche.aspectRatio(CGFloat(breite) / CGFloat(höhe), contentMode: .fit) + // **Eingepasst heisst: ganz zu sehen.** + // + // Vorher lief das Bild bis an die Bildschirmkante und damit unter + // die Dynamic Island — im Querformat frisst sie eine Ecke des + // Films. „Einpassen" und „ein Stück fehlt" schliessen sich aus; + // wer das ganze Bild will, soll es bekommen. + // + // Das kostet im Querformat gut hundert Punkte Breite. Wem das + // Bild dann zu klein ist, legt mit zwei Fingern auf **füllen** + // um — dort läuft es bewusst bis an die Kanten, und nur der + // Untertitel bleibt ausgespart. + // + // Das Polster steht **nach** dem Seitenverhältnis: so bekommt das + // Einpassen die verkleinerte Fläche vorgeschlagen und rechnet + // damit, statt hinterher beschnitten zu werden. + fläche + .aspectRatio(CGFloat(breite) / CGFloat(höhe), contentMode: .fit) + .padding(safeArea) } else { fläche }