Player: die Sprungflächen lagen über allen Knöpfen
Ein Überzug liegt in SwiftUI zwangsläufig obenauf. Ich hatte die Doppeltipp-Flächen als `.overlay` über den ganzen Player gelegt, also eine durchsichtige Fläche vor jeden Knopf — Pause, Scrubber, Qualität, Kopfzeile, alles wirkungslos. Im Stapel entscheidet dagegen die Reihenfolge, und dort liegen sie jetzt: über dem Bild, unter den Bedienelementen. Dazu kleiner. Volle Bildschirmhälften decken die Mitte ab, wo der Pause-Knopf sitzt, und den oberen Rand mit der Kopfzeile — beim Zielen auf einen Knopf träfe man zuerst die Fläche. Jetzt sind es zwei Streifen von je dreißig Prozent Breite in der mittleren Höhe, mit Abstand zu oben und unten. Tippen außerhalb zeigt und verbirgt die Bedienelemente wie zuvor. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
12bb49615b
commit
b53fce65b8
@@ -1,10 +1,6 @@
|
||||
{
|
||||
"sourceLanguage" : "en",
|
||||
"strings" : {
|
||||
"%@ GB": {
|
||||
"comment": "A picker option in the settings view that lets the user select the maximum amount of storage space that can be used for downloaded content. The value is given in gigabytes.",
|
||||
"isCommentAutoGenerated": true
|
||||
},
|
||||
"%@ → %@" : {
|
||||
"comment" : "A line in a diagnostic trace that shows a network request path and its result. The first argument is the network request path. The second argument is the network response result.",
|
||||
"isCommentAutoGenerated" : true,
|
||||
@@ -17,8 +13,8 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"1080p": {
|
||||
"comment": "A label for a 1080p video.",
|
||||
"%@ GB" : {
|
||||
"comment" : "A picker option in the settings view that lets the user select the maximum amount of storage space that can be used for downloaded content. The value is given in gigabytes.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"4K" : {
|
||||
@@ -29,8 +25,8 @@
|
||||
"comment" : "A label for a 720p video.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"SD": {
|
||||
"comment": "A label for a 720p movie.",
|
||||
"1080p" : {
|
||||
"comment" : "A label for a 1080p video.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"action.apply" : {
|
||||
@@ -2264,6 +2260,7 @@
|
||||
}
|
||||
},
|
||||
"miniPlayer.resize" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
@@ -2598,23 +2595,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"player.subtitleTracks": {
|
||||
"extractionState": "stale",
|
||||
"localizations": {
|
||||
"de": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Untertitel"
|
||||
}
|
||||
},
|
||||
"en": {
|
||||
"stringUnit": {
|
||||
"state": "translated",
|
||||
"value": "Subtitles"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"player.subtitlesOff" : {
|
||||
"comment" : "Eintrag, der die Untertitel abschaltet",
|
||||
"extractionState" : "stale",
|
||||
@@ -2633,6 +2613,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"player.subtitleTracks" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Untertitel"
|
||||
}
|
||||
},
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Subtitles"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"player.tracks" : {
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
@@ -2667,6 +2664,7 @@
|
||||
}
|
||||
},
|
||||
"quality.audioOnly" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
@@ -2732,6 +2730,7 @@
|
||||
}
|
||||
},
|
||||
"quality.current.bitrate" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
@@ -2748,6 +2747,7 @@
|
||||
}
|
||||
},
|
||||
"quality.current.dropped" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
@@ -2780,6 +2780,7 @@
|
||||
}
|
||||
},
|
||||
"quality.current.resolution" : {
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
@@ -2814,6 +2815,7 @@
|
||||
},
|
||||
"quality.kilobits %lld" : {
|
||||
"comment" : "Platzhalter: Kilobit je Sekunde",
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
@@ -2865,6 +2867,7 @@
|
||||
},
|
||||
"quality.megabits %@" : {
|
||||
"comment" : "Platzhalter: Megabit je Sekunde",
|
||||
"extractionState" : "stale",
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
@@ -2950,6 +2953,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"SD" : {
|
||||
"comment" : "A label for a 720p movie.",
|
||||
"isCommentAutoGenerated" : true
|
||||
},
|
||||
"search.hint" : {
|
||||
"localizations" : {
|
||||
"de" : {
|
||||
|
||||
@@ -43,6 +43,14 @@ struct PlayerView: View {
|
||||
.ignoresSafeArea()
|
||||
}
|
||||
|
||||
// **Unter** den Bedienelementen, nicht darüber. Als Überzug
|
||||
// über dem ganzen Player lag hier eine durchsichtige Fläche
|
||||
// vor jedem Knopf und hat sämtliche Tipper verschluckt —
|
||||
// Pause, Scrubber, Qualität, alles tot. Ein Überzug liegt in
|
||||
// SwiftUI zwangsläufig obenauf; hier im Stapel entscheidet
|
||||
// die Reihenfolge, und alles Spätere liegt darüber.
|
||||
seekZones
|
||||
|
||||
switch model.phase {
|
||||
case .preparing:
|
||||
preparing(for: model)
|
||||
@@ -90,15 +98,9 @@ struct PlayerView: View {
|
||||
.persistentSystemOverlays(showsControls ? .automatic : .hidden)
|
||||
.landscapeWhileVisible()
|
||||
.contentShape(.rect)
|
||||
// Zwei Hälften: links zurück, rechts vor. Ein einzelnes Tippen zeigt
|
||||
// weiterhin die Bedienelemente — die Reihenfolge entscheidet, sonst
|
||||
// verschluckt das einfache Tippen das doppelte.
|
||||
.overlay {
|
||||
HStack(spacing: 0) {
|
||||
seekZone(seconds: -10, symbol: "gobackward.10")
|
||||
seekZone(seconds: 10, symbol: "goforward.10")
|
||||
}
|
||||
}
|
||||
// Für alles außerhalb der Sprungflächen: Tippen zeigt und verbirgt
|
||||
// die Bedienelemente wie bisher.
|
||||
.onTapGesture { toggleControls() }
|
||||
.sheet(isPresented: $showsTrackPicker) {
|
||||
TrackPickerSheet(model: model)
|
||||
}
|
||||
@@ -370,10 +372,33 @@ struct PlayerView: View {
|
||||
.padding(FlixySpacing.section)
|
||||
}
|
||||
|
||||
/// Eine Bildschirmhälfte zum Springen.
|
||||
/// Die beiden Flächen zum Springen.
|
||||
///
|
||||
/// Bewusst ohne sichtbare Trennung: die Fläche soll sich nicht aufdrängen.
|
||||
/// Der kurze Hinweis beim Doppeltippen sagt genug.
|
||||
/// Bewusst nicht die ganzen Bildschirmhälften: in der Mitte sitzt der
|
||||
/// Pause-Knopf, oben die Kopfzeile, unten der Scrubber. Deckten die
|
||||
/// Flächen alles ab, träfe man beim Zielen auf einen Knopf zuerst die
|
||||
/// Fläche — und der Knopf bliebe wirkungslos.
|
||||
private var seekZones: some View {
|
||||
GeometryReader { geometry in
|
||||
let width = geometry.size.width * 0.3
|
||||
let height = geometry.size.height * 0.45
|
||||
|
||||
HStack(spacing: 0) {
|
||||
seekZone(seconds: -10, symbol: "gobackward.10").frame(width: width)
|
||||
Spacer(minLength: 0)
|
||||
seekZone(seconds: 10, symbol: "goforward.10").frame(width: width)
|
||||
}
|
||||
.frame(height: height)
|
||||
// Etwas unterhalb der Mitte: oben bleibt die Kopfzeile frei,
|
||||
// unten der Scrubber.
|
||||
.position(x: geometry.size.width / 2, y: geometry.size.height * 0.5)
|
||||
}
|
||||
}
|
||||
|
||||
/// Eine Fläche zum Springen.
|
||||
///
|
||||
/// Ohne sichtbare Begrenzung: die Fläche soll sich nicht aufdrängen. Der
|
||||
/// kurze Hinweis beim Doppeltippen sagt genug.
|
||||
private func seekZone(seconds: Double, symbol: String) -> some View {
|
||||
Color.clear
|
||||
.contentShape(.rect)
|
||||
|
||||
Reference in New Issue
Block a user