SwiftUI @Observable 动画失效:是使用错误还是Apple Bug?
问题描述
我有一个Toggle,原本应该触发视图出现的动画:
- 使用普通
@State时,动画正常工作("Is On"文本会带动画出现):
struct ExampleView_State: View { @State var isOn = false var body: some View { Form { Section { Toggle("Toggle", isOn: $isOn.animation()) } if isOn { Section { Text("Is On") } } } } }
- 但将状态移至由SwiftUI新的
@Observable特性控制的外部对象时,动画丢失:
@Observable class ExampleViewModel_Observation { var isOn: Bool = false } struct ExampleView_Observation: View { @State var viewModel = ExampleViewModel_Observation() var body: some View { Form { Section { Toggle("Toggle", isOn: $viewModel.isOn.animation()) } if viewModel.isOn { Section { Text("Is On") } } } } }
- 而等效的
ObservableObject实现则工作正常:
class ExampleViewModel_ObservableObject: ObservableObject { var isOn: Bool = false } struct ExampleView_ObservableObject: View { @StateObject var viewModel = ExampleViewModel_ObservableObject() var body: some View { Form { Section { Toggle("Toggle", isOn: $viewModel.isOn.animation()) } if viewModel.isOn { Section { Text("Is On") } } } } }
请问我使用@Observable/Swift Observation的方式有误,还是这是Apple的Bug?
问题原因与解决方案
这不是Apple的Bug,是你对@Observable绑定动画的用法存在问题。
@Observable生成的绑定类型与ObservableObject的绑定类型对.animation()的处理逻辑不同,不能直接在绑定路径末尾附加.animation(),可以通过以下两种方式修复:
方式1:给Toggle添加animation修饰符
将动画应用到Toggle本身,而非绑定对象上:
struct ExampleView_Observation: View { @State var viewModel = ExampleViewModel_Observation() var body: some View { Form { Section { Toggle("Toggle", isOn: $viewModel.isOn) .animation(.default, value: viewModel.isOn) } if viewModel.isOn { Section { Text("Is On") } } } } }
方式2:用withAnimation包裹状态修改
在Toggle的动作回调中,手动用withAnimation包裹状态变更操作:
struct ExampleView_Observation: View { @State var viewModel = ExampleViewModel_Observation() var body: some View { Form { Section { Toggle("Toggle", isOn: $viewModel.isOn) { newValue in withAnimation { viewModel.isOn = newValue } } } if viewModel.isOn { Section { Text("Is On") } } } } }
为什么ObservableObject可以正常工作?
ObservableObject的绑定($viewModel.isOn)是标准的Binding<Value>类型,会自动把动画关联到状态变化的传播流程中;而@Observable生成的是Observation.Binding<Value>类型,需要显式指定动画的作用位置才能触发视图过渡动画。
内容的提问来源于stack exchange,提问作者David
相关产品推荐
相关产品推荐

