import AVKit import SwiftUI import FlixyPlayback import FlixyVLC /// Die Bildfläche des laufendie Engine. /// /// Die einzige Stelle in der App, an der der Unterschied zwischen den beiden /// Engines sichtbar wird — weil beide ihr Bild auf unterschiedliche Weise /// ausliefern: AVPlayer über eine `AVPlayerLayer`, VLC in eine gewöhnliche /// `UIView`. struct VideoSurface: View { let engine: (any VideoPlayerEngine)? /// Die Bildebene des Systemplayers, gehalten vom Modell. /// /// **Nicht** hier gebaut. Eine `AVPlayerLayer`, die in `makeUIView` /// entsteht, stirbt mit jedem Identitätswechsel der Ansicht — und genau /// das passiert beim Wechsel zwischen Kachel und Vollbild. Das Bild /// setzte dann jedes Mal neu an, sichtbar als Zucken und Schwarzbild. /// VLC macht es seit jeher richtig und reicht seine `videoView` durch; /// hier ist es jetzt derselbe Weg. let surface: PlayerLayerView var body: some View { switch engine { case is AVPlayerEngine: UIViewContainer(view: surface) case let vlcEngine as VLCPlayerEngine: UIViewContainer(view: vlcEngine.videoView) default: Color.black } } } /// Eine `UIView`, deren Ebene eine `AVPlayerLayer` ist. /// /// Der übliche Weg: eine Ebene von Hand als Unterebene einzuhängen bedeutet, /// ihre Größe bei jeder Drehung selbst nachzuführen. Als Hauptebene macht das /// UIKit. final class PlayerLayerView: UIView { override static var layerClass: AnyClass { AVPlayerLayer.self } var playerLayer: AVPlayerLayer { layer as! AVPlayerLayer } } /// Hängt eine bestehende `UIView` in den SwiftUI-Baum. /// /// Umhängen statt neu bauen: dieselbe `UIView` wandert zwischen Kachel und /// Vollbild hin und her, und die Wiedergabe merkt davon nichts. private struct UIViewContainer: UIViewRepresentable { let view: UIView func makeUIView(context: Context) -> UIView { view } func updateUIView(_ uiView: UIView, context: Context) {} }