Bild-in-Bild gab es nur beim Weggehen — jetzt auch auf Zuruf
Du hast keinen Knopf gefunden, weil es keinen gab: `AVPictureInPictureController` stand da, aber nur mit `canStartPictureInPictureAutomaticallyFromInline`. Das greift, wenn man die App verlässt — und auf dem Mac gar nicht. Der Knopf erscheint nur, wenn es wirklich ginge. Das ist nicht immer so: `attachSurface` steigt für alles ausser `AVPlayerEngine` sofort aus, und `AVPictureInPictureController` braucht eine `AVPlayerLayer`. Läuft ein Titel über VLC, gibt es kein Bild-in-Bild — nur Ton im Hintergrund. Genau das war deine Beobachtung. **Beobachtet, nicht abgefragt.** `isPictureInPicturePossible` steht erst fest, wenn der Player ein Bild hat; wer beim Zeichnen fragt, bekommt `false` und zeichnet den Knopf nie wieder. Also meldet es sich über KVO — vom Input-Thread, deshalb der Sprung auf den Hauptakteur, wie bei den übrigen Beobachtungen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
261cdee2f2
commit
b1973c6890
@@ -201,6 +201,18 @@ final class PlayerModel {
|
||||
/// `@Observable` einen `lazy`-Delegaten nicht überlebt.
|
||||
@ObservationIgnored private var pictureInPicture: AVPictureInPictureController?
|
||||
@ObservationIgnored private var pictureInPictureDelegate: PictureInPictureBridge?
|
||||
@ObservationIgnored private var pictureInPictureObservation: NSKeyValueObservation?
|
||||
|
||||
/// Ob Bild-in-Bild **jetzt** ginge.
|
||||
///
|
||||
/// **Beobachtet, nicht abgefragt.** `isPictureInPicturePossible` steht erst
|
||||
/// fest, wenn der Player ein Bild hat — wer beim Zeichnen fragt, bekommt
|
||||
/// `false` und zeichnet den Knopf nie wieder. Also meldet es sich selbst.
|
||||
///
|
||||
/// Bleibt bei VLC dauerhaft falsch: `AVPictureInPictureController` braucht
|
||||
/// eine `AVPlayerLayer`, und VLC zeichnet in eine eigene Ansicht.
|
||||
private(set) var canUsePictureInPicture = false
|
||||
private(set) var isPictureInPictureActive = false
|
||||
/// Was beim Beenden von Bild-in-Bild wieder ins Vollbild führen soll.
|
||||
@ObservationIgnored private var restoreFromPictureInPicture: (() -> Void)?
|
||||
|
||||
@@ -1578,8 +1590,11 @@ final class PlayerModel {
|
||||
streamServer = nil
|
||||
streamCache = nil
|
||||
prefetchStatus = nil
|
||||
pictureInPictureObservation = nil
|
||||
pictureInPicture = nil
|
||||
pictureInPictureDelegate = nil
|
||||
canUsePictureInPicture = false
|
||||
isPictureInPictureActive = false
|
||||
surface.playerLayer.player = nil
|
||||
isPlaying = false
|
||||
isBuffering = false
|
||||
@@ -1766,6 +1781,32 @@ final class PlayerModel {
|
||||
controller?.delegate = bridge
|
||||
pictureInPictureDelegate = bridge
|
||||
pictureInPicture = controller
|
||||
// KVO meldet auf einem beliebigen Thread — deshalb der Sprung,
|
||||
// wie bei den Beobachtungen in `AVPlayerEngine`.
|
||||
pictureInPictureObservation = controller?.observe(
|
||||
\.isPictureInPicturePossible,
|
||||
options: [.initial, .new]
|
||||
) { [weak self] controller, _ in
|
||||
let moeglich = controller.isPictureInPicturePossible
|
||||
Task { @MainActor [weak self] in self?.canUsePictureInPicture = moeglich }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Schaltet Bild-in-Bild ein oder wieder aus.
|
||||
///
|
||||
/// **Von Hand, nicht nur beim Verlassen der App.** Automatisch wechselt der
|
||||
/// Player schon, wenn man zum Homebildschirm geht — aber nur dann, und auf
|
||||
/// dem Mac gar nicht. Wer das Bild klein neben etwas anderes legen will,
|
||||
/// ohne die App zu verlassen, hatte dafür keinen Weg.
|
||||
func togglePictureInPicture() {
|
||||
guard let pictureInPicture else { return }
|
||||
if pictureInPicture.isPictureInPictureActive {
|
||||
pictureInPicture.stopPictureInPicture()
|
||||
isPictureInPictureActive = false
|
||||
} else {
|
||||
pictureInPicture.startPictureInPicture()
|
||||
isPictureInPictureActive = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user