SwiftUI中如何为视图过渡与修饰器设置不同动画
解决方案
要实现位置移动用2秒慢速动画,视图进出(显示/隐藏)用0.5秒快速动画,可以通过环境动画覆盖全局动画的方式区分两种场景,无需显式withAnimation:
核心思路
- 全局隐式动画负责控制布局变化(方块上下移动)的慢速效果;
- 给需要过渡的视图容器单独设置环境动画,覆盖全局配置,专门控制视图插入/移除(显示/隐藏)的快速过渡效果。
修改后的代码
enum Pos: Int, CaseIterable { case up = 0, down, none /// 随机返回与当前不同的状态 var newValue: Pos { Pos(rawValue: (rawValue+Int.random(in: 1...2)) % 3)!} } struct ContentView: View { @State private var posArray: [Pos] = Array(repeating: Pos.none, count: 8).map{$0.newValue} var body: some View { VStack { HStack(spacing: 1) { ForEach(0 ..< posArray.count, id: \.self) { i in VStack(spacing: 0) { // 顶部占位块 if posArray[i] != .up { Color.clear.frame(width: 40, height: 40) } // 可显示/隐藏的数字方块 Group { if posArray[i] != .none { Color.primary .frame(width: 40, height: 40) .overlay { Text((i+1).formatted()) .foregroundStyle(.background) } } } .transition(.move(edge: posArray[i] == .up ? .top : .bottom) .combined(with: .opacity)) // 给过渡视图设置环境动画,覆盖全局配置 .environment(\.animation, .easeOut(duration: 0.5)) // 底部占位块 if posArray[i] != .down {Color.clear.frame(width: 40, height: 40) } } } }.padding(4).border(Color.green, width: 2) Button("Animate Change") { posArray = posArray.map{ $0.newValue } } }.padding(20) // 全局动画控制布局移动的慢速效果 .animation(.easeOut(duration: 2), value: posArray) } }
效果说明
- 位置移动(比如从.up切换到.down):由于是占位块的显示/隐藏导致布局变化,受全局
.animation(.easeOut(duration: 2))控制,动画时长2秒; - 视图显示/隐藏(比如从.none切换到.up,或从.down切换到.none):视图的插入/移除由
Group上的环境动画控制,过渡效果(移动+透明度)时长0.5秒; - 完全基于隐式动画,无需在按钮点击时使用
withAnimation,适配复杂场景需求。
内容的提问来源于stack exchange,提问作者Anton
相关产品推荐
相关产品推荐

