diff --git a/Sources/DropTwo/Views/PlayerView.swift b/Sources/DropTwo/Views/PlayerView.swift new file mode 100644 index 0000000..8306560 --- /dev/null +++ b/Sources/DropTwo/Views/PlayerView.swift @@ -0,0 +1,28 @@ +import AppKit +import AVKit +import SwiftUI + +/// Dünne Hülle um AppKits `AVPlayerView` statt AVKits SwiftUI-`VideoPlayer`. +/// +/// `VideoPlayer` crasht auf manchen macOS-Ständen beim Öffnen mit einem +/// Swift-Runtime-Fehler ("failed to demangle superclass of VideoPlayerView +/// from mangled name 'So12AVPlayerViewC'") — ein Metadata-Bug in Apples +/// eigener `_AVKit_SwiftUI`-Bridge, kein Fehler in diesem Code. `AVPlayerView` +/// direkt per `NSViewRepresentable` einzubinden umgeht den kaputten Codepfad. +struct PlayerView: NSViewRepresentable { + let player: AVPlayer + + func makeNSView(context: Context) -> AVPlayerView { + let view = AVPlayerView() + view.player = player + view.controlsStyle = .inline + view.showsFullScreenToggleButton = false + return view + } + + func updateNSView(_ nsView: AVPlayerView, context: Context) { + if nsView.player !== player { + nsView.player = player + } + } +} diff --git a/Sources/DropTwo/Views/ReviewSheet.swift b/Sources/DropTwo/Views/ReviewSheet.swift index a336cf8..b86add6 100644 --- a/Sources/DropTwo/Views/ReviewSheet.swift +++ b/Sources/DropTwo/Views/ReviewSheet.swift @@ -3,10 +3,12 @@ import DropTwoCore import SwiftUI /// Schritt 5: 9:16-Master mit eingeblendetem 4:5-Rahmen zur Framing-Kontrolle. -/// Scrubbar über die nativen AVKit-Kontrollen von `VideoPlayer`. Der Rahmen -/// nutzt exakt dieselbe Geometrie wie `CropEngine.targetSize` — was hier zu -/// sehen ist, ist tatsächlich das, was beim Crop rauskommt. Rein zur Kontrolle: -/// der Offset ist hier noch nicht verstellbar (README, "Danach"-Liste). +/// Scrubbar über die nativen Kontrollen von `PlayerView` (AppKits +/// `AVPlayerView`, nicht AVKits SwiftUI-`VideoPlayer` — siehe dort für den +/// Grund). Der Rahmen nutzt exakt dieselbe Geometrie wie +/// `CropEngine.targetSize` — was hier zu sehen ist, ist tatsächlich das, was +/// beim Crop rauskommt. Rein zur Kontrolle: der Offset ist hier noch nicht +/// verstellbar (README, "Danach"-Liste). struct ReviewSheet: View { let sourceURL: URL @Environment(\.dismiss) private var dismiss @@ -45,7 +47,7 @@ struct ReviewSheet: View { } else if let player, let videoSize, videoSize.height > 0 { GeometryReader { proxy in ZStack { - VideoPlayer(player: player) + PlayerView(player: player) cropOverlay(sourceSize: videoSize, viewSize: proxy.size) } }