在UIViewController中声明@Published属性传递给SwiftUI的变化监听实现
直接结论
完全可以实现你描述的状态跨侧同步逻辑,但你原代码无法编译的核心问题是错把SwiftUI专属的@State属性包装器用在了UIViewController类中。@State是专门为值类型的SwiftUI视图设计的状态存储,它的初始化逻辑强依赖SwiftUI的视图上下文,根本不支持在引用类型的UIKit控制器中正常工作——用在UIViewController里首先就会触发编译报错,就算强行绕过编译,既没法触发Combine订阅,也没法生成合法的双向绑定值传给SwiftUI。
正确实现逻辑
你要的“控制器持有状态、把双向绑定传给SwiftUI视图、SwiftUI侧修改值时控制器可收到通知”的链路,完全可以通过Combine的@Published实现,核心是替换掉错用的@State:
- 在UIViewController中把待同步的状态标记为
@Published属性 - 手动将
@Published的属性桥接为SwiftUI可识别的Binding实例,传给UIHostingController承载的SwiftUI视图 - 在VC的初始化流程中通过Combine订阅
@Published的变化流,接收SwiftUI侧的修改通知
对应的可运行代码示例:
class MyVC: UIViewController { // 替换掉原代码里错用的@State @Published var myBoolState: Bool = false private var subscribers = Set<AnyCancellable>() override func viewDidLoad() { super.viewDidLoad() // 订阅状态变更,这里会收到包括初始值在内的所有状态更新 $myBoolState .sink { newState in // 在这里处理SwiftUI侧传回的状态变化 print("状态更新为:\(newState)") } .store(in: &subscribers) } func createTheView() { // 把@Published属性桥接为SwiftUI可用的Binding,不能直接把$myBoolState传给SwiftUI let stateBinding = Binding( get: { self.myBoolState }, set: { self.myBoolState = $0 } ) let hostingVC = UIHostingController( rootView: MySwiftUIView(myBoolState: stateBinding) ) navigationController?.pushViewController(hostingVC, animated: true) } } struct MySwiftUIView: View { // 这部分原代码写法完全正确,不需要修改 @Binding var myBoolState: Bool var body: some View { Button(action: { myBoolState = true }) { Text("Push Me") } } }
工程优化提示
- 如果不想每次创建HostingController都手写
Binding的get/set闭包,可以单独抽一个遵循ObservableObject的状态类,把所有@Published的待同步状态都存在这个类里,VC和SwiftUI视图都引用同一个状态实例,既简化绑定代码,也方便状态的统一管理 @Published的发布流会在订阅建立时自动发送一次当前属性值,如果不需要初始值触发回调,可以在sink调用前链式加上.dropFirst()过滤掉初始事件
内容的提问来源于stack exchange,提问作者Joris
相关产品推荐
相关产品推荐

