SwiftUI 中 AVPlayerLayer 的帧大小更新

AVPlayerLayer's frame size update in SwiftUI

我有一个异步加载视频并通过另一个 UIViewRepresentable 视图呈现的视图。 GeometryReader 提供视频容器的框架,然后用于初始化 AVPlayerLayer。当设备方向改变时出现问题(因此,容器的框架也改变了),并且 AVPlayerLayer 不会重新布局自身以匹配容器。我尝试在 updateUIView 内更新图层的框架,甚至调用 setNeedsLayoutsetNeedsDisplay 到图层和 uiView,但没有任何改变。

如何调整图层以匹配容器的大小?

struct VideoView: View {

    @StateObject var videoLoader: VideoLoader

    @ViewBuilder
    var body: some View {
        if let player = videoLoader.player {
            GeometryReader { geometry in
                let frame = geometry.frame(in: .local)
                VideoPlayerView(frame: frame, player: player)
                    .frame(width: frame.width, height: frame.height)
                    .onAppear { player.play() }
                    .onDisappear { player.pause() }
            }
        }
    }

}

struct VideoPlayerView: UIViewRepresentable {

    private let frame: CGRect
    private let playerLayer: AVPlayerLayer

    init(frame: CGRect, player: AVPlayer) {
        self.frame = frame
        self.playerLayer = AVPlayerLayer(player: player)
        self.playerLayer.frame = frame
    }

    func makeUIView(context: Context) -> UIView {
        let container = UIView()
        container.layer.addSublayer(playerLayer)
        return container
    }

    func updateUIView(_ uiView: UIView, context: Context) {
        playerLayer.frame.size = uiView.frame.size
    }

}

解决方案是创建自定义 UIView 并在 layoutSubviews() 中更新图层的帧大小。

struct VideoPlayerView: UIViewRepresentable {

    class PlayerContainer: UIView {

        init(playerLayer: AVPlayerLayer) {
            super.init(frame: .zero)
            layer.addSublayer(playerLayer)
        }

        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }

        override func layoutSubviews() {
            super.layoutSubviews()
            layer.sublayers?.first?.frame = frame
        }

    }

    private let playerLayer: AVPlayerLayer

    init(player: AVPlayer) {
        playerLayer = AVPlayerLayer(player: player)
    }

    func makeUIView(context: Context) -> UIView {
        return PlayerContainer(playerLayer: playerLayer)
    }

    func updateUIView(_ uiView: UIView, context: Context) { }

}