为何SwiftUI中@State属性无法从外部读取?
为什么SwiftUI的@State属性变更无法被外部读取?
我只想读取而非修改SwiftUI视图的@State属性值,该属性会在.onAppear中被修改,但此变更无法被外部感知,这是为什么?
我了解@State和@ObservedObject的区别:@State用于内部管理状态,@ObservedObject用于外部管理状态。但我仅需读取值,为何无法实现?且返回值始终为nil,这并非时机问题。
class MyController : UIHostingController<SwiftUIView> { public func getText() -> String? { rootView.text } } struct SwiftUIView: View { @State public var text: String? var body: some View { Text(text ?? "Hello World") .background(Color.red) .onAppear { text = "so und so" } } } class ViewController: UIViewController { var subs = Set<AnyCancellable>() var vc: MyController! override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) vc = MyController(rootView: SwiftUIView()) vc.view.backgroundColor = UIColor.green addChild(vc) view.addSubview(vc.view) vc.view.frame = view.bounds print("text = \(vc.getText() ?? "nil")") Timer.publish(every: 1, on: .current, in: .default) .autoconnect() .receive(on: RunLoop.main) .sink { _ in print("text = \(self.vc.getText() ?? "nil")") } .store(in: &subs) } }
核心原因:@State的特殊存储机制
@State属性的实际存储不在SwiftUIView结构体实例内部,而是由SwiftUI框架在结构体实例之外的独立内存区域托管。你通过rootView.text访问的只是结构体里的一个“占位符”,并非真正存储状态值的位置。
当你在.onAppear中修改text时,SwiftUI会更新框架托管的实际状态值,同时触发视图重渲染。但由于SwiftUIView是值类型,重渲染会创建新的结构体实例——你持有的rootView还是最初创建的旧实例,它的text始终是初始的nil,自然读不到更新后的值。
可行的解决办法
如果需要外部读取SwiftUI视图的内部状态,推荐以下几种符合SwiftUI设计模式的方案:
1. 改用@ObservableObject + @Published
把状态转移到一个ObservableObject类中,外部通过持有该类实例直接读取值:
class TextViewModel: ObservableObject { @Published var text: String? } struct SwiftUIView: View { @ObservedObject var viewModel: TextViewModel var body: some View { Text(viewModel.text ?? "Hello World") .background(Color.red) .onAppear { viewModel.text = "so und so" } } } // 外部读取方式 let viewModel = TextViewModel() let vc = MyController(rootView: SwiftUIView(viewModel: viewModel)) print(viewModel.text) // 可直接获取更新后的值
2. 通过闭包主动回调
在SwiftUI视图中定义闭包,当状态变更时主动通知外部:
struct SwiftUIView: View { @State private var text: String? var onTextUpdate: ((String?) -> Void)? var body: some View { Text(text ?? "Hello World") .background(Color.red) .onAppear { text = "so und so" onTextUpdate?(text) } .onChange(of: text) { newValue in onTextUpdate?(newValue) } } } // 外部初始化时绑定回调 let swiftUIView = SwiftUIView { updatedText in print("外部收到状态更新:\(updatedText ?? "nil")") }
3. 避免直接访问rootView
永远不要通过UIHostingController的rootView直接读取状态,这违反了SwiftUI的状态管理封装原则,只会导致不可预期的问题。
内容的提问来源于stack exchange,提问作者Dominik
相关产品推荐
相关产品推荐

