如何不使用.offset modifier实现SwiftUI视图移动动画?
实现SwiftUI视图平滑左右移动动画
要让视图在左右槽之间平滑移动,核心是让SwiftUI识别到同一个视图在不同位置的变化,而非销毁重建。你之前的写法虽然给了相同的id,但视图分属两个独立的ZStack容器,SwiftUI会默认把它当成两个不同视图做显隐过渡,而非移动动画。这里可以用matchedGeometryEffect实现预期效果,无需手动调整offset。
修改后的完整代码
import SwiftUI struct Sign: Identifiable { let id = UUID() let text: String } struct SignView: View { let sign: Sign let namespace: Namespace.ID // 用于关联视图的命名空间 var body: some View { Text(sign.text) .frame(width: 75, height: 25, alignment: .center) .foregroundStyle(Color.white) .background(Color.blue) .matchedGeometryEffect(id: sign.id, in: namespace) // 核心:绑定命名空间与匹配ID } } class SignManager: ObservableObject { @Published var leftSlot: Sign? @Published var rightSlot: Sign? } struct MotionTestView: View { @ObservedObject var manager = SignManager() @Namespace private var signNamespace // 创建命名空间 var body: some View { VStack { HStack { ZStack { Text("Empty") .frame(width: 75, height: 25, alignment: .center) .foregroundStyle(Color.white) .background(Color.blue.opacity(0.3)) if let sign = manager.leftSlot { SignView(sign: sign, namespace: signNamespace) } } ZStack { Text("Empty") .frame(width: 75, height: 25, alignment: .center) .foregroundStyle(Color.white) .background(Color.blue.opacity(0.3)) if let sign = manager.rightSlot { SignView(sign: sign, namespace: signNamespace) } } } Button("Swap", action: { withAnimation { (manager.leftSlot, manager.rightSlot) = (manager.rightSlot, manager.leftSlot) } }) } .onAppear { manager.leftSlot = Sign(text: "Test") manager.rightSlot = nil } } }
关键修改点说明
- 命名空间(Namespace):创建
@Namespace变量作为视图位置关联的标识,确保SwiftUI能识别两个位置的视图是同一个实体。 - matchedGeometryEffect:给
SignView添加该修饰符,用sign.id作为匹配ID并绑定到同一命名空间。当视图在左右槽间切换时,SwiftUI会自动计算位置差并生成平滑移动动画。 - 移除原代码中手动添加的
.id(sign.id),因为matchedGeometryEffect已包含视图身份识别逻辑。
修改后点击Swap按钮,"Test"视图会从左槽平滑移动到右槽,完全符合需求。
内容的提问来源于stack exchange,提问作者Christian Stevenson
相关产品推荐
相关产品推荐

