使用Hosting Controller时SwiftUI View的@State变量不更新问题
问题原因
你遇到的问题核心在于值类型特性和SwiftUI状态管理规则:
TestTimerFile是struct值类型,创建UIHostingController(rootView: testTimerFile)时会生成原实例的副本,而非引用原对象。- TestVC的定时器修改的是原
testTimerFile实例的@State变量,但HostingController显示的是独立副本,视图无法收到更新通知。 - Timer闭包默认运行在后台线程,直接修改SwiftUI状态可能导致更新不及时。
解决方案
改用ObservableObject统一管理定时器状态,让TestVC和HostingController共享同一个状态实例,同时确保状态更新在主线程执行。
修改后的代码示例
1. 重构状态管理(新增TimerViewModel)
创建ObservableObject类管理定时器状态,替代原View内部的@State:
import SwiftUI class TimerViewModel: ObservableObject { @Published var timeRemaining = 30 func startTimer() { // 确保在主线程更新状态 guard Thread.isMainThread else { DispatchQueue.main.async { self.startTimer() } return } print(timeRemaining) if timeRemaining > 0 { timeRemaining -= 1 } else { timeRemaining = 30 } } }
2. 更新TestTimerFile视图
让视图依赖共享的ObservableObject实例:
import SwiftUI struct TestTimerFile: View { @ObservedObject var viewModel: TimerViewModel var body: some View { Text("\(viewModel.timeRemaining)") .onAppear { viewModel.startTimer() } } }
3. 修改TestVC逻辑
持有共享的TimerViewModel实例,传给HostingController的rootView,同时优化Timer的生命周期:
import UIKit import SwiftUI class TestVC: UIViewController { let timerViewModel = TimerViewModel() var hostingController: UIHostingController<TestTimerFile>! var timer: Timer? override func viewDidLoad() { super.viewDidLoad() // 初始化HostingController时传入共享的viewModel hostingController = UIHostingController(rootView: TestTimerFile(viewModel: timerViewModel)) addChild(hostingController) view.addSubview(hostingController.view) hostingController.view.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ hostingController.view.topAnchor.constraint(equalTo: view.topAnchor), hostingController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), hostingController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor), hostingController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor) ]) hostingController.didMove(toParent: self) // 使用weak self避免循环引用 timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in self?.timerViewModel.startTimer() } } deinit { // 销毁Timer防止内存泄漏 timer?.invalidate() } }
4. RoomUIView保持不变
原RoomUIView代码无需修改,它仅负责创建TestVC实例。
关键改动说明
- 用
ObservableObject+@Published替代View内部的@State,实现UIKit与SwiftUI的状态共享。 - 强制在主线程更新状态,避免线程冲突导致的视图不更新。
- 添加
[weak self]和Timer销毁逻辑,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Abdullah Ajmal
相关产品推荐
相关产品推荐

