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

