SwiftUI中@Binding未触发didSet,如何确认数据源已更新?
为什么@Binding修改@State变量时不会触发didSet?如何确认值已同步回数据源?
问题复现
以下代码中,两个按钮都能更新tapCount的界面显示,但只有直接修改tapCount的按钮会触发didSet打印,通过@Binding修改的按钮不会触发:
struct Tapper: View { @Binding var incrementMe: Int var body: some View { Button("Button With Binding") { incrementMe += 1 } } } struct ContentView: View { @State private var tapCount: Int = 0 { didSet{ print("didSet tapCount = \(tapCount)") } } var body: some View { VStack { Text("tapCount: \(tapCount)") Tapper(incrementMe: $tapCount) .padding(4.0) Button("Button Without Binding") { tapCount += 1 } } .padding() } }
原因分析
@State和@Binding的底层实现是直接操作变量的底层存储值,而非通过属性的标准setter方法:
- 直接修改
@State变量时,调用的是属性的setter,因此会触发didSet回调; - 通过
@Binding修改时,Binding会绕过属性的setter,直接更新@State的底层存储,所以didSet不会被执行。
确认值同步的替代方案
如果需要监听@State变量的变化(无论修改来源),可以使用以下方法:
1. 使用.onChange修饰符
这是SwiftUI官方推荐的监听值变化的方式,无论值是通过@Binding还是直接修改,只要发生变化就会触发:
struct ContentView: View { @State private var tapCount: Int = 0 { didSet{ print("didSet tapCount = \(tapCount)") } } var body: some View { VStack { Text("tapCount: \(tapCount)") Tapper(incrementMe: $tapCount) .padding(4.0) Button("Button Without Binding") { tapCount += 1 } } .padding() .onChange(of: tapCount) { newValue in print("tapCount updated to: \(newValue)") } } }
2. 自定义ObservableObject(复杂场景)
如果需要更复杂的状态管理,可以将tapCount放到自定义的ObservableObject中,通过@Published属性监听变化:
class TapCounter: ObservableObject { @Published var tapCount: Int = 0 { didSet { print("didSet tapCount = \(tapCount)") } } } struct ContentView: View { @StateObject private var counter = TapCounter() var body: some View { VStack { Text("tapCount: \(counter.tapCount)") Tapper(incrementMe: $counter.tapCount) .padding(4.0) Button("Button Without Binding") { counter.tapCount += 1 } } .padding() } }
这种方式下,无论是直接修改还是通过@Binding修改,@Published都会触发didSet(因为@Published的底层会监听属性的setter)。
内容的提问来源于stack exchange,提问作者Murray Sagal
相关产品推荐
相关产品推荐

