Der Mac ist nicht immer Catalyst

Die Bedienung mit Zeiger und Tastatur hing an
`#if targetEnvironment(macCatalyst)`. Das ist in dem Bau, der tatsächlich
benutzt wird, **falsch**: „für iPad angepasst" ist buchstäblich der
iOS-Bau, der auf Apple Silicon startet, und dort ist die Abfrage nicht
erfüllt. Klick-zum-Anhalten, Leertaste und die Pfeiltasten waren damit
genau dort abgeschaltet, wo sie gebraucht werden.

Jetzt beantwortet `FlixyPlatform.isMac` die Frage zur Laufzeit und kennt
beide Wege. Die Ansicht bleibt dabei eine einzige: `focusable(_:)` nimmt
den Schalter entgegen, die Tastenhandler geben sonst `.ignored` zurück.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-09-15 20:40:43 +02:00
co-authored by Claude Opus 5
parent 9a4b6b1e06
commit 37a6c2648f
2 changed files with 52 additions and 26 deletions
+28 -26
View File
@@ -32,6 +32,8 @@ struct PlayerView: View {
@State private var hintTask: Task<Void, Never>?
/// Ob die Tastatur bei diesem Bildschirm ankommt. Nur auf dem Mac gesetzt.
@FocusState private var keyboardFocus: Bool
/// Ob Zeiger und Tastatur gelten. Einmal beim Start ermittelt.
private var isMac: Bool { FlixyPlatform.isMac }
/// Wann der Zeiger zuletzt die Bedienelemente geholt hat.
@State private var lastPointerReveal: Date = .distantPast
@@ -124,36 +126,28 @@ struct PlayerView: View {
//
// Dort ist beides selbstverständlich: Leertaste hält an, die Pfeile
// springen, und die Bedienelemente kommen von allein, sobald sich der
// Zeiger regt. Auf dem iPhone gibt es keines von beidem, und
// `focusable` legte dort nur einen Fokusrahmen über den Film.
#if targetEnvironment(macCatalyst)
.focusable()
// Zeiger regt. Auf dem iPhone gibt es keines von beidem.
//
// Zur Laufzeit entschieden, nicht beim Übersetzen: „für iPad
// angepasst" ist der iOS-Bau und läuft trotzdem auf dem Mac. Ein
// `#if` hätte das Ganze genau dort abgeschaltet. `focusable(_:)`
// nimmt den Schalter entgegen, die Tastenhandler geben sonst
// `.ignored` zurück — so bleibt es **eine** Ansicht, und der Player
// wird nicht neu aufgebaut, nur weil die Frage vom Gerät abhängt.
.focusable(isMac)
.focusEffectDisabled()
.focused($keyboardFocus)
// Von Hand gesetzt, weil sonst niemand den Fokus hat: der Player
// besteht aus Knöpfen, und solange keiner angeklickt wurde, ginge
// jeder Tastendruck ins Leere.
.onAppear { keyboardFocus = true }
.onKeyPress(.space) {
model.togglePlayPause()
revealControls()
return .handled
}
.onKeyPress(.leftArrow) {
seek(by: -Double(settings.rewindSeconds))
revealControls()
return .handled
}
.onKeyPress(.rightArrow) {
seek(by: Double(settings.forwardSeconds))
revealControls()
return .handled
}
.onAppear { if isMac { keyboardFocus = true } }
.onKeyPress(.space) { onMac { model.togglePlayPause() } }
.onKeyPress(.leftArrow) { onMac { seek(by: -Double(settings.rewindSeconds)) } }
.onKeyPress(.rightArrow) { onMac { seek(by: Double(settings.forwardSeconds)) } }
.onContinuousHover { phase in
guard case .active = phase else { return }
guard isMac, case .active = phase else { return }
pointerMoved()
}
#endif
.sheet(item: $trackPicker) { kind in
TrackPickerSheet(model: model, kind: kind)
}
@@ -602,12 +596,20 @@ struct PlayerView: View {
/// ein Tipp, der sofort anhält, nähme den einzigen Weg zu Scrubber,
/// Tonspur und Qualität.
private func primaryTap() {
#if targetEnvironment(macCatalyst)
guard isMac else {
toggleControls()
return
}
model.togglePlayPause()
revealControls()
#else
toggleControls()
#endif
}
/// Führt eine Tastenaktion aus — aber nur dort, wo es eine Tastatur gibt.
private func onMac(_ action: () -> Void) -> KeyPress.Result {
guard isMac else { return .ignored }
action()
revealControls()
return .handled
}
private func toggleControls() {