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

SwiftUI中List内视图应用matchedGeometryEffect后部分项消失原因

问题描述

尝试使用.matchedGeometryEffect实现主视图到编辑视图的转场动画时遇到两个问题:

  • 若直接给List中的Text(item.name)添加该修饰符,仅一个列表项可见,其余项被隐藏;
  • 若给List本身添加该修饰符,所有列表项正常显示且转场动画生效,但动画效果不符合预期。

请问为何给Text(item.name)添加.matchedGeometryEffect会导致List中多数项消失?

ContentView

struct ContentView: View {
    @State var showEditView = false
    @State var selectedItem:Item?
    @Namespace var editViewAnaimation
    
    let items = [Item(name: "Fork", symbolName: "fork.knife"),
                Item(name: "Hammer", symbolName: "hammer.fill"),
                Item(name: "Lightbulb", symbolName: "lightbulb.fill")]
    
    var body: some View {
        VStack {
            if showEditView{
                Button(action: {
                    withAnimation{
                        showEditView.toggle()
                    }
                }, label: {
                    Image(systemName: "xmark.square.fill")
                        .foregroundColor(Color.red)
                })
                EditView( editViewAnaimation: editViewAnaimation, selectedItem: selectedItem!)
            }else{
                List(items, id:\.self){ item in
                    HStack{
                        Image(systemName: item.symbolName)
                            .foregroundColor(Color.red)
                        Text(item.name)
                            .foregroundColor(Color.blue)
                           /// some items disappear when applied here, why?
                           //.matchedGeometryEffect(id: "name", in: editViewAnaimation)
                    }
                    .onTapGesture {
                        selectedItem = item
                        withAnimation{
                            showEditView.toggle()
                        }
                    }
                }
                /// all items show when applied here but not the effect I want
                .matchedGeometryEffect(id: "name", in: editViewAnaimation)
            }
        }
    }
}

EditView

struct EditView: View {
    var editViewAnaimation: Namespace.ID
    let selectedItem:Item
    @State var nameInput = ""
    
    var body: some View {
        ScrollView{
            TextField("name", text: $nameInput)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .matchedGeometryEffect(id: "name", in: editViewAnaimation)
        }
        .padding()
        .onAppear{
            nameInput = selectedItem.name
        }
    }
}
问题原因与解决方案

原因分析

.matchedGeometryEffect的核心规则是:在同一个命名空间(Namespace.ID)下,同一个id只能对应一个视图实例。

当你给List中所有Text(item.name)都添加id: "name"的修饰符时,所有Text视图共享同一个id,SwiftUI会将它们视为同一个视图的不同表现形式,只会渲染其中一个(通常是列表的最后一项),其余项就会被隐藏。

解决方案

给每个列表项的Text设置唯一的id,同时让编辑视图的TextField使用被选中项对应的id,这样只有被选中的Text和TextField会建立匹配关系,既保证所有列表项正常显示,又能实现预期的转场动画。

修改后的代码

ContentView中修改Text的修饰符

Text(item.name)
    .foregroundColor(Color.blue)
    .matchedGeometryEffect(id: item.name, in: editViewAnaimation) // 使用item的name作为唯一id

EditView中修改TextField的修饰符

TextField("name", text: $nameInput)
    .textFieldStyle(RoundedBorderTextFieldStyle())
    .matchedGeometryEffect(id: selectedItem.name, in: editViewAnaimation) // 匹配选中项的id

原理说明

每个列表项的Text拥有唯一的id,SwiftUI会将它们视为独立的视图,因此所有项都能正常显示。当用户选中某一项时,编辑视图的TextField会使用该选中项的id,与对应的Text建立几何匹配关系,转场时就会只针对选中的Text和TextField执行动画,完全符合预期效果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 09:43:11