Mac: näher am iPad — Schrift, Seitenleiste, Formulare
Drei Gründe, warum der Mac anders aussah, und keiner davon war Geschmack. **Die Schrift.** macOS rendert dieselben Textstile deutlich kleiner als iOS: `largeTitle` misst dort 26 Punkt statt 34, Fließtext 13 statt 17. Eine Oberfläche, die auf dem iPad stimmt, wirkt damit zusammengeschrumpft. Auf dem Mac stehen jetzt feste Größen — die von iOS. Das kostet dort Dynamic Type; vertretbar, weil macOS keine systemweite Textgröße kennt und man ein Fenster stattdessen größer ziehen kann. **Der Aufmacher wächst mit.** Das war der zweite, größere Teil: das Bild ist so breit wie das Fenster, die Schrift darauf war es nicht. Bei 1280 Punkt Breite wirkt ein 34-Punkt-Titel verloren, bei 834 auf dem iPad genau richtig. Titel, Schriftzug, Angaben und Abstände richten sich jetzt nach der Breite des Aufmachers — mit dem iPad als Bezug und einem Deckel, damit ein Fenster über die ganze Wand nicht in Plakatschrift endet. Die Beschreibung bekommt außerdem eine Zeilenbreite: ein Satz über 1280 Punkt ist unlesbar, das Auge verliert die Zeile. **Die Seitenleiste.** Sie richtet sich nach ihrem Inhalt, und bei Wörtern wie „Start" fällt sie auf eine Breite zusammen, in der schon der Kontoname darunter nicht mehr Platz hatte — daher das „S…". Die Beschriftungen haben jetzt eine Mindestbreite. **Die Formulare.** Auf dem Mac nahm SwiftUI den Spalten-Stil: Beschriftungen rechts ausgerichtet, keine abgesetzten Abschnitte. Jetzt derselbe gruppierte Stil wie auf iOS. Dazu: Scrollbalken sind weg. Der graue Streifen unter dem Aufmacher kam daher, dass in den Systemeinstellungen „Scrollbalken immer einblenden" steht — dagegen reicht `.hidden` nicht, `.never` schon. Und das Fenster öffnet mit 1280 × 820 statt in Notizzettelgröße, ohne graue Titelleiste. 263 Tests grün, iOS baut unverändert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
d54954b9c2
commit
f8ada44a8c
@@ -39,13 +39,25 @@ public struct HeroBanner<Actions: View>: View {
|
||||
self.actions = actions()
|
||||
}
|
||||
|
||||
/// Wie stark Schrift und Schriftzug mitwachsen.
|
||||
///
|
||||
/// Der Aufmacher ist so breit wie das Fenster. Bei fester Schriftgröße
|
||||
/// wirkt derselbe Titel auf einem iPad groß und in einem breiten
|
||||
/// Mac-Fenster verloren — das Bild wächst, die Schrift nicht. Bezugsgröße
|
||||
/// ist die Breite eines iPads im Hochformat; alles darüber wächst mit,
|
||||
/// aber gedeckelt, damit ein Fenster über die ganze Wand nicht in
|
||||
/// Plakatschrift endet.
|
||||
private func scale(for width: CGFloat) -> CGFloat {
|
||||
min(max(width / 834, 1), 1.7)
|
||||
}
|
||||
|
||||
/// Schriftzug, wenn vorhanden — sonst gesetzter Titel.
|
||||
///
|
||||
/// Der Schriftzug wird ohne Bild-Transcoder geladen; er ist ein
|
||||
/// freigestelltes PNG, und der Transcoder liefert JPEG. Aus dem
|
||||
/// freigestellten Logo würde sonst ein schwarzer Kasten.
|
||||
@ViewBuilder
|
||||
private var titleView: some View {
|
||||
private func titleView(scale: CGFloat) -> some View {
|
||||
if let logoURL {
|
||||
AsyncImage(url: logoURL) { phase in
|
||||
switch phase {
|
||||
@@ -56,23 +68,23 @@ public struct HeroBanner<Actions: View>: View {
|
||||
// Nach oben begrenzt: manche Schriftzüge sind fast
|
||||
// quadratisch, andere sehr breit. Ohne Deckel schöbe
|
||||
// ein hohes Logo die Knöpfe aus dem Bild.
|
||||
.frame(maxWidth: 260, maxHeight: 76, alignment: .leading)
|
||||
.frame(maxWidth: 260 * scale, maxHeight: 76 * scale, alignment: .leading)
|
||||
default:
|
||||
// Solange geladen wird — und wenn es scheitert — bleibt es
|
||||
// beim gesetzten Titel. Ein leerer Platz wäre schlimmer
|
||||
// als eine Systemschrift.
|
||||
plainTitle
|
||||
plainTitle(scale: scale)
|
||||
}
|
||||
}
|
||||
.accessibilityLabel(Text(title))
|
||||
} else {
|
||||
plainTitle
|
||||
plainTitle(scale: scale)
|
||||
}
|
||||
}
|
||||
|
||||
private var plainTitle: some View {
|
||||
private func plainTitle(scale: CGFloat) -> some View {
|
||||
Text(title)
|
||||
.font(.flixyHero)
|
||||
.font(.system(size: 34 * scale, weight: .bold))
|
||||
.foregroundStyle(Color.flixyTextPrimary)
|
||||
.lineLimit(2)
|
||||
}
|
||||
@@ -106,29 +118,43 @@ public struct HeroBanner<Actions: View>: View {
|
||||
.allowsHitTesting(false)
|
||||
}
|
||||
.overlay(alignment: .bottomLeading) {
|
||||
VStack(alignment: .leading, spacing: FlixySpacing.snug) {
|
||||
titleView
|
||||
GeometryReader { geometry in
|
||||
let scale = scale(for: geometry.size.width)
|
||||
|
||||
if let metadata {
|
||||
Text(metadata)
|
||||
.font(.flixyCaption)
|
||||
.foregroundStyle(Color.flixyTextSecondary)
|
||||
}
|
||||
VStack(alignment: .leading, spacing: FlixySpacing.snug * scale) {
|
||||
titleView(scale: scale)
|
||||
|
||||
if let summary {
|
||||
Text(summary)
|
||||
.font(.flixyCaption)
|
||||
.foregroundStyle(Color.flixyTextSecondary)
|
||||
.lineLimit(2)
|
||||
}
|
||||
if let metadata {
|
||||
Text(metadata)
|
||||
.font(.system(size: 12 * scale))
|
||||
.foregroundStyle(Color.flixyTextSecondary)
|
||||
}
|
||||
|
||||
HStack(spacing: FlixySpacing.regular) {
|
||||
actions
|
||||
if let summary {
|
||||
Text(summary)
|
||||
.font(.system(size: 12 * scale))
|
||||
.foregroundStyle(Color.flixyTextSecondary)
|
||||
.lineLimit(2)
|
||||
// Ein Satz über die ganze Breite eines großen
|
||||
// Fensters ist unlesbar; das Auge verliert die
|
||||
// Zeile. Achtzig Zeichen sind die Grenze, ab
|
||||
// der Satzspiegel unangenehm werden.
|
||||
.frame(maxWidth: 640 * scale, alignment: .leading)
|
||||
}
|
||||
|
||||
HStack(spacing: FlixySpacing.regular) {
|
||||
actions
|
||||
}
|
||||
.padding(.top, FlixySpacing.tight)
|
||||
}
|
||||
.padding(.top, FlixySpacing.tight)
|
||||
.padding(.horizontal, FlixySpacing.screenMargin * scale)
|
||||
.padding(.bottom, FlixySpacing.loose * scale)
|
||||
.frame(
|
||||
width: geometry.size.width,
|
||||
height: geometry.size.height,
|
||||
alignment: .bottomLeading
|
||||
)
|
||||
}
|
||||
.padding(.horizontal, FlixySpacing.screenMargin)
|
||||
.padding(.bottom, FlixySpacing.loose)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user