You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI嵌入UIViewController无法更新UIButton/PlayerLayer帧的问题求助

PlayerView.swift

struct PlayerView : UIViewControllerRepresentable {

    @Binding var screenCommnit : Bool 
    @Binding var screenWidth : CGFloat
    @Binding var screenHeight : CGFloat
    let controller = MyViewController()
    
    func makeUIViewController(context: Context) -> MyViewController {
        print("makeUIViewController")
        return controller
    }

    func updateUIViewController(_ uiViewController: MyViewController, context: Context) {
        print("updateUIViewController")
        controller.screenCommnit = screenCommnit
        controller.screenWidth = screenWidth
        controller.screenHeight = screenHeight
        controller.delegate = context.coordinator
      
        let size = controller.view.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
        controller.preferredContentSize = size
    }
    
    func makeCoordinator() -> PlayerView.Coordinator {
        Coordinator(url:$url, screenCommnit:$screenCommnit,screenWidth:$screenWidth,screenHeight:$screenHeight)
    }
}

MyViewController.swift

class MyViewController: UIViewController {
    
    var url: String = ""
    var screenCommnit : Bool = false
    var screenWidth: CGFloat = UIScreen.main.bounds.width 
    var screenHeight: CGFloat = 260

    var fullScreenBtn = UIButton()
    var uiView = UIView()
    var delegate : PlayerViewDelegate?

    override func viewDidLoad() {
        super.viewDidLoad()
        print("viewDidLoad...")
        self.view.backgroundColor = .white
        self.view.frame = CGRect(x: 0, y: 0, width:self.screenWidth, height: self.screenHeight)
        self.PlayerLayer()
        self.ScreenButton()
    }
  
    func ScreenButton(){
        print("ScreenButton...")
        fullScreenBtn.frame = CGRect.init(x: -20, y: 30, width: 200, height: 30)
        fullScreenBtn.setTitle("全屏", for: .normal)
        fullScreenBtn.setImage(UIImage(systemName: "arrow.up.and.down.circle"), for:.normal)
        fullScreenBtn.tag = 1
        fullScreenBtn.addTarget(self,action:#selector(btnClick), for: .touchUpInside)
        self.view.insertSubview(fullScreenBtn, at:1)
    }

    func PlayerLayer(){
        print("PlayerLayer...")
        let playerLayer = AVPlayerLayer(player: AVPlayer(url: URL(string:  "https://media.w3.org/2010/05/sintel/trailer.mp4")!))
        print("PlayerLayer() : \(self.view.frame.width) \(self.view.frame.height)")
        playerLayer.frame = CGRect.init(x: 0, y: 9, width: screenWidth, height: screenHeight)
        playerLayer.frame.size.height = playerLayer.frame.size.height * 0.92
        playerLayer.videoGravity = .resizeAspect
        playerLayer.backgroundColor = UIColor.black.cgColor
        playerLayer.player?.play()
        self.view.layer.insertSublayer(playerLayer, at: 0)
    }
    
    @objc func btnClick(btn: UIButton) {
        screenCommnit = !screenCommnit
        delegate?.OneGroupDidFinished(self) 
    }

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }
}

解决方案

核心问题分析

  1. viewDidLoad仅执行一次,后续screenWidth/screenHeight更新时,没有重新触发视图、按钮和PlayerLayer的帧更新逻辑
  2. AVPlayerLayer创建后未保存引用,无法在尺寸变化时修改它的帧
  3. PlayerView的updateUIViewController仅更新了controller的属性,没有触发实际的视图刷新

具体修复步骤

1. 修改MyViewController,添加帧更新方法并保存PlayerLayer引用

class MyViewController: UIViewController {
    
    var url: String = ""
    var screenCommnit : Bool = false
    var screenWidth: CGFloat = UIScreen.main.bounds.width 
    var screenHeight: CGFloat = 260

    var fullScreenBtn = UIButton()
    var uiView = UIView()
    var delegate : PlayerViewDelegate?
    // 保存PlayerLayer引用,用于后续更新
    private var playerLayer: AVPlayerLayer?

    override func viewDidLoad() {
        super.viewDidLoad()
        print("viewDidLoad...")
        self.view.backgroundColor = .white
        // 初始化时调用更新方法
        updateAllFrames()
        self.PlayerLayer()
        self.ScreenButton()
    }
    
    // 新增:统一更新所有元素的帧
    func updateAllFrames() {
        // 更新主视图尺寸
        self.view.frame = CGRect(x: 0, y: 0, width: self.screenWidth, height: self.screenHeight)
        // 更新全屏按钮的位置(可根据需求调整相对布局)
        fullScreenBtn.frame = CGRect(x: -20, y: 30, width: 200, height: 30)
        // 更新PlayerLayer尺寸
        if let layer = playerLayer {
            layer.frame = CGRect(x: 0, y: 9, width: screenWidth, height: screenHeight)
            layer.frame.size.height = layer.frame.size.height * 0.92
        }
    }
  
    func ScreenButton(){
        print("ScreenButton...")
        fullScreenBtn.setTitle("全屏", for: .normal)
        fullScreenBtn.setImage(UIImage(systemName: "arrow.up.and.down.circle"), for:.normal)
        fullScreenBtn.tag = 1
        fullScreenBtn.addTarget(self,action:#selector(btnClick), for: .touchUpInside)
        self.view.insertSubview(fullScreenBtn, at:1)
        // 移除这里的frame设置,统一交给updateAllFrames处理
    }

    func PlayerLayer(){
        print("PlayerLayer...")
        let playerLayer = AVPlayerLayer(player: AVPlayer(url: URL(string:  "https://media.w3.org/2010/05/sintel/trailer.mp4")!))
        playerLayer.videoGravity = .resizeAspect
        playerLayer.backgroundColor = UIColor.black.cgColor
        playerLayer.player?.play()
        self.view.layer.insertSublayer(playerLayer, at: 0)
        // 保存PlayerLayer引用
        self.playerLayer = playerLayer
        // 初始frame交给updateAllFrames设置
    }
    
    @objc func btnClick(btn: UIButton) {
        screenCommnit = !screenCommnit
        delegate?.OneGroupDidFinished(self) 
    }

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }
}

2. 修改PlayerView,在updateUIViewController中触发帧更新

struct PlayerView : UIViewControllerRepresentable {

    @Binding var screenCommnit : Bool 
    @Binding var screenWidth : CGFloat
    @Binding var screenHeight : CGFloat
    let controller = MyViewController()
    
    func makeUIViewController(context: Context) -> MyViewController {
        print("makeUIViewController")
        return controller
    }

    func updateUIViewController(_ uiViewController: MyViewController, context: Context) {
        print("updateUIViewController")
        controller.screenCommnit = screenCommnit
        controller.screenWidth = screenWidth
        controller.screenHeight = screenHeight
        controller.delegate = context.coordinator
      
        // 触发controller更新所有元素的帧
        controller.updateAllFrames()
        // 直接设置preferredContentSize,适配SwiftUI布局
        controller.preferredContentSize = CGSize(width: screenWidth, height: screenHeight)
    }
    
    func makeCoordinator() -> PlayerView.Coordinator {
        Coordinator(url:$url, screenCommnit:$screenCommnit,screenWidth:$screenWidth,screenHeight:$screenHeight)
    }
}

3. 修正PlayVideo中的语法错误

初始化screenWidth时补充乘号:

@State private var screenWidth: CGFloat = UIScreen.main.bounds.width * UIScreen.main.scale

内容的提问来源于stack exchange,提问作者jy02411368

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 22:26:59