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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 10:27:46