- PlayerNSView: AVPlayerLayer as sublayer (not layer= replacement) NSView internally manages a sublayer array; replacing the backing layer directly causes dangling refs during autorelease pool drain - cleanup(): only pause + remove observers, no replaceCurrentItem CoreMedia internal state gets corrupted when item is replaced during view teardown; let it release naturally with deinit
83 lines
2.1 KiB
Swift
83 lines
2.1 KiB
Swift
import SwiftUI
|
||
import AVFoundation
|
||
|
||
// MARK: - 跨平台AVPlayer渲染
|
||
|
||
#if os(iOS)
|
||
import UIKit
|
||
|
||
struct VideoPlayerRepresentable: UIViewRepresentable {
|
||
let player: AVPlayer
|
||
|
||
func makeUIView(context: Context) -> PlayerUIView {
|
||
let view = PlayerUIView()
|
||
view.player = player
|
||
return view
|
||
}
|
||
|
||
func updateUIView(_ uiView: PlayerUIView, context: Context) {
|
||
uiView.player = player
|
||
}
|
||
}
|
||
|
||
class PlayerUIView: UIView {
|
||
override static var layerClass: AnyClass { AVPlayerLayer.self }
|
||
|
||
var player: AVPlayer? {
|
||
get { (layer as? AVPlayerLayer)?.player }
|
||
set { (layer as? AVPlayerLayer)?.player = newValue }
|
||
}
|
||
|
||
override var contentMode: UIView.ContentMode {
|
||
get { (layer as? AVPlayerLayer)?.videoGravity == .resizeAspectFill ? .scaleAspectFill : .scaleAspectFit }
|
||
set {
|
||
(layer as? AVPlayerLayer)?.videoGravity = newValue == .scaleAspectFill ? .resizeAspectFill : .resizeAspect
|
||
}
|
||
}
|
||
}
|
||
|
||
#elseif os(macOS)
|
||
import AppKit
|
||
|
||
struct VideoPlayerRepresentable: NSViewRepresentable {
|
||
let player: AVPlayer
|
||
|
||
func makeNSView(context: Context) -> PlayerNSView {
|
||
let view = PlayerNSView()
|
||
view.player = player
|
||
return view
|
||
}
|
||
|
||
func updateNSView(_ nsView: PlayerNSView, context: Context) {
|
||
nsView.player = player
|
||
}
|
||
}
|
||
|
||
class PlayerNSView: NSView {
|
||
private let playerLayer = AVPlayerLayer()
|
||
|
||
override init(frame: NSRect) {
|
||
super.init(frame: frame)
|
||
wantsLayer = true
|
||
playerLayer.videoGravity = .resizeAspect
|
||
}
|
||
|
||
required init?(coder: NSCoder) { fatalError() }
|
||
|
||
override func layout() {
|
||
super.layout()
|
||
// 确保 playerLayer 作为 sublayer 存在(不要在 init 中 addSublayer,
|
||
// 因为 wantsLayer=true 后 layer 可能还没创建)
|
||
if playerLayer.superlayer !== layer {
|
||
layer?.addSublayer(playerLayer)
|
||
}
|
||
playerLayer.frame = bounds
|
||
}
|
||
|
||
var player: AVPlayer? {
|
||
get { playerLayer.player }
|
||
set { playerLayer.player = newValue }
|
||
}
|
||
}
|
||
#endif
|