You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何不使用.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 08:34:55