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

SwiftUI中ScrollView保留List样式NavigationLink与LazyVGrid懒加载方案

问题成因

  • 直接将LazyVGrid嵌入List时,List作为基于UITableView封装的容器,会提前计算所有子视图的高度做渲染优化,会强制一次性加载LazyVGrid内的全部元素,直接破坏LazyVGrid的懒加载机制。
  • List中NavigationLink自带的右侧披露箭头、行分隔线、标准内边距属于List容器注入的环境样式,当外层容器换成ScrollView后,这些默认样式会直接失效,NavigationLink会退化为普通按钮样式。

实现方案

根据项目适配的系统版本和性能要求,二选一即可:

方案1:ScrollView 容器方案(性能最优,兼容全版本)

该方案完全保留LazyVGrid的懒加载特性,通过自定义行视图1:1复刻List中NavigationLink的默认样式,无任何性能损耗。

  1. 先定义和系统原生样式完全一致的导航行组件:
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
        )
    }
}
  1. 页面整体用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 14:03:27