iOS 16下SwiftUI将Binding传入ViewModel后Toggle异常问题求助
iOS 16 SwiftUI Binding 传入ViewModel后UI更新异常问题分析与解决
问题现象
在iOS 16中,将Binding作为参数传入ViewModel后出现以下异常:
- 点击绑定了ViewModel中
Binding的Toggle(testA)时,能修改对应状态值(同步到testB的状态),但自身UI不更新 - 直接绑定
@State属性的Toggle(testB)可以正常关闭,但开启时需要点击两次才能生效
该逻辑在iOS 15中可正常运行。
问题原因
iOS 16对SwiftUI的状态更新追踪机制做了优化调整:
- 当
Binding被存储在ViewModel中,而ViewModel又被包裹在Loadable枚举里作为@State对象holder的属性时,SwiftUI无法正确追踪到这个间接Binding的状态变化。因为holder.loadable本身没有发生改变,iOS 16不会触发视图刷新,导致testA的UI无法同步状态。 - testB需要点击两次的问题,是因为第一次点击后状态实际已变化,但视图未刷新,UI仍显示旧状态,第二次点击时才完成状态与UI的同步。
解决方案
推荐采用ObservableObject替代直接存储Binding的方式,让状态变化能被SwiftUI正确追踪:
修改后的代码示例
import SwiftUI enum Loadable<T> { case loaded(T) case notRequested } struct Holder { var loadable: Loadable<ContentView.ViewModel> = .notRequested } struct ContentView: View { @State var holder = Holder() // 使用@StateObject管理状态,确保生命周期与视图绑定 @StateObject private var toggleState = ToggleState() var body: some View { switch holder.loadable { case .notRequested: Text("") .onAppear { holder.loadable = .loaded(.init(toggleState: toggleState)) } case .loaded(let viewModel): Toggle("testA", isOn: $toggleState.isOn) Toggle("testB", isOn: $toggleState.isOn) } } struct ViewModel { let toggleState: ToggleState } class ToggleState: ObservableObject { @Published var isOn = false } }
方案说明
- 将原本的
toggler状态抽离为独立的ObservableObject子类ToggleState,用@Published标记状态属性,确保状态变化时能发送通知。 - 视图中用
@StateObject持有ToggleState实例,保证其生命周期与视图一致。 - ViewModel只需持有
ToggleState的引用,视图通过$toggleState.isOn直接绑定状态,SwiftUI能正确追踪状态变化并刷新UI。
补充说明
如果一定要保留原有Holder结构和Binding使用方式,也可以通过手动触发holder的变化来强制刷新视图,比如在修改toggler时同时修改holder的某个无关属性,但这种方式不够优雅,不推荐长期使用。
内容的提问来源于stack exchange,提问作者Vollan
相关产品推荐
相关产品推荐

