SwiftUI:如何为ScrollView中的每个列表单元格单独应用MatchedGeometryEffect
解决方案
你当前把matchedGeometryEffect加在ForEach外层,所有单元格共用了同一个id: "shape",无法实现单个单元格的独立匹配效果,按下面步骤调整即可:
调整步骤
- 将
matchedGeometryEffect修饰符移动到ForEach循环内部的单个单元格根视图上 - 给每个单元格设置唯一的匹配id,和当前循环的country绑定,确保同一个国家的常规样式、备选大尺寸样式使用完全相同的id和同一个命名空间
- 切换
showAlternate状态时用withAnimation包裹,触发过渡动画
修改后参考代码
struct ContentView: View { @Binding var countries: [Country] @State var showAlternate = false @Namespace var namespace var body: some View { NavigationView { ScrollView { // LazyVStack移到外层避免重复创建,优化性能 LazyVStack { ForEach(Array(countries.enumerated()), id: \.1.id) { (index,country) in Group { if !showAlternate { // 原常规单元格样式 HStack { NavigationLink( destination: CountryView(country: country), label: { HStack { Image(country.image) .resizable() .scaledToFit() Text(country.display_name) .foregroundColor(Color.black) .padding(.leading) Spacer() } .padding(.top, 12.0) .frame(height: 80) } ).buttonStyle(FlatLinkStyle()) } .padding(.horizontal, 16.0) .overlay(Divider(), alignment: .top) } else { // 后续实现的备选大尺寸样式,保持id和常规状态一致 HStack { // 大尺寸布局代码 } .padding(.horizontal, 16.0) } } .background(Rectangle().opacity(0.2) // 每个单元格用和country绑定的唯一id .matchedGeometryEffect(id: "country_bg_\(country.id)", in: namespace) ) } } } } .font(Font.custom("Avenir", size: 22)) // 切换状态示例,必须加动画包裹才会触发过渡效果 .toolbar { Button("切换样式") { withAnimation { showAlternate.toggle() } } } } }
注意事项
- 同一个元素的两种状态(常规/放大)必须使用完全相同的id和同一个
namespace才能匹配成功 - 几何匹配的两个视图层级尽量对应,避免出现异常形变效果
内容的提问来源于stack exchange,提问作者user17503091
相关产品推荐
相关产品推荐

