Der Sicherheitsrand am Bild war falsch — er schützte vor nichts

Eingepasst bekam das Bild ein Polster in Grösse des Sicherheitsrandes,
damit es nicht unter die Dynamic Island läuft. Nachgerechnet hält das
nicht.

Querformat auf einem Pro Max: 956 × 440 Punkt. Ein Bild in 16∶9 wird bei
voller Höhe 782 Punkt breit — es bleiben 87 Punkte Balken auf jeder
Seite, und die Island braucht 62. Das Bild kommt ihr von selbst nicht
nahe. Das Polster hat also nichts abgewehrt und dabei vierzig Punkte
Höhe gekostet, also jedes zehnte Bildpunktband:

    vorher   695,7 × 400 pt   Balken 130,0 / 130,3
    jetzt    765,3 × 440 pt   Balken  95,3 /  95,3

Beides über VLC gemessen, dieselbe Datei, dieselbe Lage.

Erst was breiter ist als der Schirm (2,17∶1) läuft an die Kanten — bei
21∶9 über die ganze Breite, und dann liegt ein Streifen hinter der
Island. Dafür ist der Zoomknopf da. Ein Film, der nie an den Rand darf,
wäre der schlechtere Tausch: er kostet bei *jeder* Datei Höhe, um bei
wenigen einen Streifen zu retten.

`EdgeInsets.symmetrical` fällt damit weg, samt seiner fünf Tests. Es
hatte genau eine Aufgabe — ein unsymmetrisches Polster mittig zu halten
—, und das Polster gibt es nicht mehr. Eine geprüfte Funktion, die
niemand ruft, ist trotzdem nur Ballast.

iOS und tvOS bauen, 460 Tests grün.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-10-01 07:33:18 +02:00
co-authored by Claude Opus 5
parent b0db4f8f5f
commit 3ed682dd00
3 changed files with 13 additions and 106 deletions
+13 -20
View File
@@ -864,30 +864,23 @@ struct PlayerView: View {
} else if let breite = model.statistics?.width,
let höhe = model.statistics?.height,
breite > 0, höhe > 0 {
// **Eingepasst heisst: ganz zu sehen.**
// **Eingepasst heisst: der ganze Schirm, nur das Verhältnis
// bleibt.** Kein Sicherheitsrand — hier stand einer, und er war
// falsch.
//
// 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.
// Nachgerechnet, Querformat auf einem Pro Max: 956 × 440 Punkt.
// Ein Bild in 16∶9 wird bei voller Höhe 782 Punkt breit, es
// bleiben also 87 Punkte Balken auf jeder Seite — und die Dynamic
// Island braucht 62. Das Bild kommt ihr von selbst nicht nahe.
// Das Polster schützte vor nichts und kostete vierzig Punkte
// Höhe, also jedes zehnte Bildpunktband.
//
// 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.
// Erst was breiter ist als der Schirm (2,17∶1) läuft an die
// Kanten — bei 21∶9 über die ganze Breite, und dann liegt ein
// Streifen hinter der Island. Dafür gibt es den Zoomknopf; ein
// Film, der nie an den Rand darf, wäre der schlechtere Tausch.
fläche
.aspectRatio(CGFloat(breite) / CGFloat(höhe), contentMode: .fit)
// **Gleichgemacht, nicht roh.** Der Sicherheitsrand ist
// unsymmetrisch — die Notch sitzt auf einer Seite. Mit den
// rohen Werten rückt man das Bild nicht ein, man
// **verschiebt** es um die halbe Differenz; gemessen waren
// das vierunddreissig Punkte nach rechts und elfeinhalb nach
// unten. Siehe `EdgeInsets.symmetrical`.
.padding(safeArea.symmetrical)
} else {
fläche
}
@@ -1,28 +0,0 @@
import SwiftUI
extension EdgeInsets {
/// Dieselben Ränder, aber auf beiden Seiten gleich — der jeweils grössere.
///
/// **Wofür das da ist.** Ein Sicherheitsrand ist selten symmetrisch: im
/// Querformat sitzt die Notch auf **einer** Seite, also zweiundsechzig
/// Punkte links und null rechts; im Hochformat sind es zweiundsechzig oben
/// und vierunddreissig unten. Wer damit ein Bild einrückt, rückt es nicht
/// ein — er **verschiebt** es, und zwar um die halbe Differenz.
///
/// Genau das war zu sehen: einunddreissig Punkte nach rechts im
/// Querformat, vierzehn nach unten im Hochformat. Gemessen an den
/// Bildschirmfotos: vierunddreissig und elfeinhalb.
///
/// **Für Bild, nicht für Knöpfe.** Ein Film soll mittig stehen, auch wenn
/// er dafür auf der freien Seite ein paar Punkte Balken mehr bekommt. Eine
/// Bedienleiste soll dagegen an der Kante kleben — die nimmt weiter die
/// echten, unsymmetrischen Werte.
public var symmetrical: EdgeInsets {
let senkrecht = max(top, bottom)
let waagerecht = max(leading, trailing)
return EdgeInsets(
top: senkrecht, leading: waagerecht,
bottom: senkrecht, trailing: waagerecht
)
}
}
@@ -1,58 +0,0 @@
import SwiftUI
import Testing
@testable import FlixyDesign
@Suite("Ränder gleichmachen")
struct SymmetricInsetsTests {
/// **Der Fall aus dem Querformat.** Notch links, nichts rechts — mit den
/// rohen Werten stünde das Bild einunddreissig Punkte zu weit rechts.
@Test("Die Notch-Seite bestimmt beide Seiten")
func landscapeNotch() {
let roh = EdgeInsets(top: 0, leading: 62, bottom: 21, trailing: 0)
let gleich = roh.symmetrical
#expect(gleich.leading == 62)
#expect(gleich.trailing == 62)
#expect(gleich.top == 21)
#expect(gleich.bottom == 21)
}
/// Und aus dem Hochformat: oben zweiundsechzig, unten vierunddreissig.
@Test("Oben und unten wird der grössere genommen")
func portrait() {
let gleich = EdgeInsets(top: 62, leading: 0, bottom: 34, trailing: 0).symmetrical
#expect(gleich.top == 62)
#expect(gleich.bottom == 62)
#expect(gleich.leading == 0)
#expect(gleich.trailing == 0)
}
/// Was schon gleich ist, bleibt gleich — sonst würde die Rechnung auf
/// einem iPad ohne Notch Balken erfinden, wo keine hingehören.
@Test("Symmetrisches bleibt unverändert")
func alreadySymmetric() {
let roh = EdgeInsets(top: 20, leading: 50, bottom: 20, trailing: 50)
#expect(roh.symmetrical == roh)
}
@Test("Ohne Ränder bleibt es ohne")
func zero() {
let gleich = EdgeInsets().symmetrical
#expect(gleich == EdgeInsets())
}
/// **Die Zusage, auf die es ankommt.** Egal wie schief der Rand ankommt —
/// danach ist die Mitte des verbleibenden Platzes die Mitte des Schirms.
@Test("Die Mitte bleibt die Mitte")
func centreStaysCentred() {
for roh in [
EdgeInsets(top: 0, leading: 62, bottom: 21, trailing: 0),
EdgeInsets(top: 62, leading: 0, bottom: 34, trailing: 0),
EdgeInsets(top: 5, leading: 1, bottom: 99, trailing: 40),
] {
let g = roh.symmetrical
#expect(g.leading - g.trailing == 0)
#expect(g.top - g.bottom == 0)
}
}
}