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

如何让Matched Geometry Effect仅在选中时执行单向动画?

实现Matched Geometry Effect单向选中动画

要只保留选中时的动画、取消选中时无动画,核心是仅在选中状态激活时触发动画逻辑,取消时直接跳过动画过渡。以下是两种实用方案:

方案一:条件控制匹配几何修饰符

通过判断项的选中状态,仅在选中时应用matchedGeometryEffect,并配合withAnimation只在选中触发时执行动画:

struct ContentView: View {
    @Namespace private var animationNamespace
    @State private var selectedItemId: String?
    
    let items = ["Item 1", "Item 2", "Item 3"]
    
    var body: some View {
        VStack {
            // 选中后的放大视图
            if let selectedId = selectedItemId {
                Text(selectedId)
                    .font(.largeTitle)
                    .padding()
                    .background(Color.blue)
                    .cornerRadius(10)
                    .matchedGeometryEffect(id: selectedId, in: animationNamespace)
            }
            
            // 列表项
            LazyVStack {
                ForEach(items, id: \.self) { item in
                    Text(item)
                        .padding()
                        .background(Color.gray)
                        .cornerRadius(10)
                        .onTapGesture {
                            if selectedItemId == item {
                                // 取消选中时直接修改状态,不触发动画
                                selectedItemId = nil
                            } else {
                                // 选中时触发动画
                                withAnimation {
                                    selectedItemId = item
                                }
                            }
                        }
                        // 仅当前项被选中时,作为动画源应用修饰符
                        .matchedGeometryEffect(id: item, in: animationNamespace, isSource: selectedItemId == item)
                }
            }
        }
        .padding()
    }
}

方案二:用withoutAnimation抑制取消动画

如果需要始终保留修饰符,可在取消选中时用withoutAnimation包裹状态修改,直接跳过动画:

struct ContentView: View {
    @Namespace private var animationNamespace
    @State private var selectedItemId: String?
    
    let items = ["Item 1", "Item 2", "Item 3"]
    
    var body: some View {
        VStack {
            if let selectedId = selectedItemId {
                Text(selectedId)
                    .font(.largeTitle)
                    .padding()
                    .background(Color.blue)
                    .cornerRadius(10)
                    .matchedGeometryEffect(id: selectedId, in: animationNamespace)
            }
            
            LazyVStack {
                ForEach(items, id: \.self) { item in
                    Text(item)
                        .padding()
                        .background(Color.gray)
                        .cornerRadius(10)
                        .onTapGesture {
                            if selectedItemId == item {
                                // 取消时禁用动画
                                withoutAnimation {
                                    selectedItemId = nil
                                }
                            } else {
                                // 选中时正常触发动画
                                withAnimation {
                                    selectedItemId = item
                                }
                            }
                        }
                        .matchedGeometryEffect(id: item, in: animationNamespace)
                }
            }
        }
        .padding()
    }
}

两种方案都能实现「选中时平滑动画,取消时直接消失」的效果,可根据代码结构偏好选择。

内容的提问来源于stack exchange,提问作者Peter Warbo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 00:37:14