如何让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
相关产品推荐
相关产品推荐

