SwiftUI自定义@State:如何完善update方法以模拟原生@State?
模拟SwiftUI的@State属性包装器
我已经实现了一个基础的MyStates属性包装器(遵循DynamicProperty),但update()方法的实现不完整,想知道如何模拟真正的@State,让属性变化时自动触发视图刷新。
原始代码:
@propertyWrapper struct MyStates:DynamicProperty{ init(wrappedValue:String){ UserDefaults.standard.set(wrappedValue, forKey: "myString") } var wrappedValue:String{ nonmutating set{UserDefaults.standard.set(newValue, forKey: "myString")} get{UserDefaults.standard.string(forKey: "myString") ?? ""} } var projectedValue:Binding<String>{ Binding<String>( get:{String(self.wrappedValue)}, set:{ self.wrappedValue = $0 } ) } func update() { print("render again") } }
完整实现方案
要模拟真正的@State,核心是让属性变化时能通知SwiftUI刷新视图。我们可以结合ObservableObject来管理内部状态,同时正确实现DynamicProperty的update方法:
import SwiftUI import Combine @propertyWrapper struct MyState: DynamicProperty { // 内部用ObservableObject管理状态变化 private class StateObserver: ObservableObject { @Published var value: String init(initialValue: String, key: String) { // 优先从UserDefaults读取已有值,没有则用初始值 self.value = UserDefaults.standard.string(forKey: key) ?? initialValue // 把初始值同步到UserDefaults(如果之前没有的话) if UserDefaults.standard.string(forKey: key) == nil { UserDefaults.standard.set(initialValue, forKey: key) } } } private let key: String @StateObject private var observer: StateObserver init(wrappedValue: String, key: String = "myString") { self.key = key self._observer = StateObject(wrappedValue: StateObserver(initialValue: wrappedValue, key: key)) } var wrappedValue: String { get { observer.value } nonmutating set { observer.value = newValue UserDefaults.standard.set(newValue, forKey: key) } } var projectedValue: Binding<String> { $observer.value } func update() { // SwiftUI会在视图刷新时调用此方法,这里无需额外操作,@StateObject已自动处理状态订阅 // 若有自定义需求(如日志、额外状态同步)可在此添加逻辑 } }
关键说明
- 状态订阅:通过内部的
StateObserver(遵循ObservableObject)和@Published属性,实现状态变化的自动通知,SwiftUI会感知到@Published的变化并刷新视图。 - UserDefaults同步:初始化时优先读取已有存储值,修改
wrappedValue时同步更新UserDefaults,保证数据持久化。 - update方法:
DynamicProperty的update方法会在视图刷新周期被调用,这里因为@StateObject已处理状态绑定,无需额外逻辑。 - 投影值:直接返回
$observer.value即可得到正确的Binding,无需手动实现Binding的get/set。
使用示例
struct ContentView: View { @MyState(wrappedValue: "初始值") var myText var body: some View { VStack { Text(myText) TextField("输入内容", text: $myText) } .padding() } }
内容的提问来源于stack exchange,提问作者LiangWang
相关产品推荐
相关产品推荐

