SwiftUI中如何在移除视图前应用其内置动画效果?
问题分析
当removable开关启用时,点击按钮会立即将视图从ForEach的数据源中移除(因为unanimatedNames会排除已加入animatedNames的项),导致SwiftUI无法对视图的背景色变化应用动画——视图在动画执行前就已经脱离了视图层级。
解决方案
我们需要让视图先完成背景色动画,再延迟将其从列表中移除。具体实现如下:
struct SelfAnimatingView: View { let name: String let animate: Bool var body: some View { Text(name) .padding() .foregroundStyle(.white) .background(animate ? .red : .blue) } } struct ContentView: View { @State private var removable = false @State private var names = ["Ted", "Barney", "Lily", "Robin", "Marshal"] @State private var animatingNames = Set<String>() @State private var removedNames = Set<String>() // 显示所有未被永久移除的项 private var displayedNames: [String] { names.filter { !removedNames.contains($0) } } var body: some View { VStack { Toggle("Removable", isOn: $removable).padding() ForEach(displayedNames, id: \.self) { name in Button { animatingNames.insert(name) // 若启用移除功能,在动画完成后再标记为永久移除 if removable { // 匹配SwiftUI默认动画时长(约0.3秒) DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { removedNames.insert(name) animatingNames.remove(name) } } } label: { SelfAnimatingView(name: name, animate: animatingNames.contains(name)) } // 为背景色变化单独应用动画 .animation(.default, value: animatingNames.contains(name)) } Spacer() } // 为列表项的移除应用延迟动画 .animation(.default.delay(1), value: displayedNames) } }
效果说明
- 当
removable关闭时:点击按钮仅触发背景色从蓝色到红色的动画,视图会保留在列表中。 - 当
removable开启时:点击按钮先执行背景色动画,0.3秒后将视图标记为永久移除,触发列表项的延迟移除动画(1秒延迟与原代码保持一致)。
这样就能确保视图在被视觉移除前,完整执行其内置的背景色动画。
内容的提问来源于stack exchange,提问作者Mojtaba Hosseini
相关产品推荐
相关产品推荐

