iOS16+Xcode12下TabView选中项更新:didSet失效疑问及临时方案
问题:iOS16、Xcode12环境下SwiftUI TabView中@State变量的didSet不生效?
在iOS16搭配Xcode12的环境中,使用SwiftUI的TabView时,绑定到selection的@State变量的didSet代码块完全不执行,比如下面代码里的UserDefaults.activeTab = selected从未被调用:
@State var selected: Int { didSet { UserDefaults.activeTab = selected // 永远不会执行 } } var body: some View { TabView(selection: $selected) { Text("food") .tabItem { Label("food", systemImage: "bolt.fill") } .tag(0) Text("wtr") .tabItem { Label("water", systemImage: "drop.fill") } .tag(1) } }
目前找到的临时解决方法是在每个Tab页的onAppear回调中设置UserDefaults:
@State var selected: Int var body: some View { TabView(selection: $selected) { Text("food") .tabItem { Label("food", systemImage: "bolt.fill") } .tag(0) .onAppear{ UserDefaults.activeTab = 0 } Text("wtr") .tabItem { Label("water", systemImage: "drop.fill") } .tag(1) .onAppear{ UserDefaults.activeTab = 1 } } }
解答
为什么didSet不生效?
SwiftUI的@State变量由框架直接管理底层存储,当TabView通过绑定更新selected值时,SwiftUI会直接修改底层存储,不会经过属性的setter方法,因此didSet代码块不会被触发。这是@State的设计特性,并非bug。
更可靠的解决方法
方法1:使用.onChange监听值变化
直接在TabView上添加.onChange修饰符,监听selected的变化,每次切换Tab时都会执行代码,比onAppear更可靠(避免页面重复出现时重复执行的问题):
@State var selected: Int = 0 // 记得给初始值 var body: some View { TabView(selection: $selected) { Text("food") .tabItem { Label("food", systemImage: "bolt.fill") } .tag(0) Text("wtr") .tabItem { Label("water", systemImage: "drop.fill") } .tag(1) } .onChange(of: selected) { newValue in UserDefaults.activeTab = newValue } }
方法2:改用ObservableObject管理状态
如果需要更复杂的状态管理,可以把selected放到一个ObservableObject类中,利用@Published属性的特性来监听变化:
class TabState: ObservableObject { @Published var selected: Int { didSet { UserDefaults.activeTab = selected } } init() { // 从UserDefaults初始化值 self.selected = UserDefaults.activeTab } } struct YourView: View { @StateObject private var tabState = TabState() // iOS14+可用 var body: some View { TabView(selection: $tabState.selected) { Text("food") .tabItem { Label("food", systemImage: "bolt.fill") } .tag(0) Text("wtr") .tabItem { Label("water", systemImage: "drop.fill") } .tag(1) } } }
关于临时方法的说明
你使用的onAppear方法可以工作,但有局限性:如果用户通过其他方式(比如代码里直接修改selected值)切换Tab,onAppear不会触发,导致UserDefaults没有更新。另外,当Tab页被系统后台回收后重新出现时,onAppear会再次执行,可能造成不必要的重复写入。
内容的提问来源于stack exchange,提问作者aaronium112
相关产品推荐
相关产品推荐

