SwiftUI中ScrollView保留List样式NavigationLink与LazyVGrid懒加载方案
问题成因
- 直接将
LazyVGrid嵌入List时,List作为基于UITableView封装的容器,会提前计算所有子视图的高度做渲染优化,会强制一次性加载LazyVGrid内的全部元素,直接破坏LazyVGrid的懒加载机制。 List中NavigationLink自带的右侧披露箭头、行分隔线、标准内边距属于List容器注入的环境样式,当外层容器换成ScrollView后,这些默认样式会直接失效,NavigationLink会退化为普通按钮样式。
实现方案
根据项目适配的系统版本和性能要求,二选一即可:
方案1:ScrollView 容器方案(性能最优,兼容全版本)
该方案完全保留LazyVGrid的懒加载特性,通过自定义行视图1:1复刻List中NavigationLink的默认样式,无任何性能损耗。
- 先定义和系统原生样式完全一致的导航行组件:
struct ListStyleNavLink<Destination: View, Content: View>: View { let destination: Destination let content: () -> Content init(destination: Destination, @ViewBuilder content: @escaping () -> Content) { self.destination = destination self.content = content } var body: some View { NavigationLink(destination: destination) { HStack(spacing: 8) { content() .foregroundColor(.primary) Spacer() Image(systemName: "chevron.right") .font(.system(size: 13, weight: .semibold)) .foregroundColor(Color(UIColor.tertiaryLabel)) } .padding(.vertical, 11) .padding(.horizontal, 16) .contentShape(Rectangle()) } .buttonStyle(.plain) .background( Rectangle() .fill(Color(UIColor.separator)) .frame(height: 0.5) .offset(y: 22), alignment: .bottom ) } }
- 页面整体用
ScrollView+LazyVStack搭建,网格部分的懒加载完全正常:
ScrollView { LazyVStack(spacing: 0, pinnedViews: []) { ForEach(items) { item in ListStyleNavLink(destination: MyDestination()) { Text("Navigation link text") } } LazyVGrid(columns: columns, spacing: 12) { ForEach(gridItems) { gridItem in MyGridItem() } } .padding(.horizontal, 16) .padding(.top, 12) } }
补充:如果需要和系统List一致的点击灰度反馈,可以给行增加按压状态的背景透明度变化,效果和原生无差异。
方案2:List 容器方案(无需自定义样式,仅适配iOS16+)
如果不想自定义导航行样式,且项目最低支持iOS16,可以通过修改List行属性,关闭List对网格区域的预计算,保留LazyVGrid的懒加载能力:
List { Section { ForEach(items) { item in NavigationLink(destination: MyDestination()) { Text("Navigation link text") } } } Section { LazyVGrid(columns: columns, spacing: 12) { ForEach(gridItems) { gridItem in MyGridItem() } } .listRowInsets(.init(top: 12, leading: 16, bottom: 12, trailing: 16)) .listRowSeparator(.hidden) } } .listStyle(.insetGrouped) .environment(\.defaultMinListRowHeight, 0)
注意:iOS16以下版本使用该方案时,List仍然会提前计算网格全量内容高度,数据量超过50项就会出现明显的首屏卡顿,大流量场景优先选择方案1。
内容的提问来源于stack exchange,提问作者user1795832
相关产品推荐
相关产品推荐

