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

SwiftUI:如何实现随子视图宽度变化的自适应列数动态网格?

SwiftUI 自适应列数网格(适配动态文本缩放)

核心实现思路

通过监听系统动态文本尺寸,结合屏幕可用宽度计算适配的列数,使用LazyVGrid构建网格,确保单元格填满宽度且文本不截断,同时保证每行单元格数量统一。

单元格视图实现

先定义包含图片和文本的单元格,确保文本能自适应高度、不截断:

struct GridItemView: View {
    let imageName: String
    let text: String
    
    var body: some View {
        VStack(spacing: 8) {
            Image(systemName: imageName)
                .resizable()
                .aspectRatio(contentMode: .fit)
                .frame(height: 40)
            Text(text)
                .multilineTextAlignment(.center)
                .fixedSize(horizontal: false, vertical: true) // 允许文本换行,高度自适应
        }
        .padding()
        .frame(maxWidth: .infinity) // 填满对应列宽
        .background(Color.gray.opacity(0.1))
        .cornerRadius(8)
    }
}

自适应网格主视图

通过dynamicTypeSize监听文本缩放,结合屏幕宽度计算列数,构建统一列数的网格:

struct AdaptiveGridView: View {
    // 模拟3列5行的数据源
    let items: [(image: String, text: String)] = [
        ("1.circle", "项目1"), ("2.circle", "项目2"), ("3.circle", "项目3"),
        ("4.circle", "项目4"), ("5.circle", "项目5"), ("6.circle", "项目6"),
        ("7.circle", "项目7"), ("8.circle", "项目8"), ("9.circle", "项目9"),
        ("10.circle", "项目10"), ("11.circle", "项目11"), ("12.circle", "项目12"),
        ("13.circle", "项目13"), ("14.circle", "项目14"), ("15.circle", "项目15")
    ]
    
    // 监听系统动态文本尺寸
    @Environment(\.dynamicTypeSize) private var dynamicTypeSize
    
    var body: some View {
        GeometryReader { geometry in
            ScrollView {
                LazyVGrid(columns: gridColumns(for: geometry.size.width), spacing: 16) {
                    ForEach(items, id: \.text) { item in
                        GridItemView(imageName: item.image, text: item.text)
                    }
                }
                .padding()
            }
        }
    }
    
    // 根据文本尺寸和屏幕宽度计算列数
    private func gridColumns(for width: CGFloat) -> [GridItem] {
        let minColumnWidth: CGFloat
        // 按动态文本尺寸区间设置最小列宽阈值
        switch dynamicTypeSize {
        case .xSmall, .small, .medium:
            minColumnWidth = 100 // 默认小文本,3列布局
        case .large, .xLarge:
            minColumnWidth = 150 // 文本增大,自动切换2列
        case .xxLarge, .xxxLarge, .accessibility1...:
            minColumnWidth = 200 // 大文本/无障碍模式,切换单列
        default:
            minColumnWidth = 100
        }
        
        // 计算列数,确保至少1列
        let columnCount = max(1, Int(width / minColumnWidth))
        return Array(repeating: GridItem(.flexible(), spacing: 16), count: columnCount)
    }
}

关键说明

  • 利用@Environment(\.dynamicTypeSize)精准捕获文本缩放级别,不同级别对应不同的最小列宽,触发列数切换
  • GeometryReader获取屏幕可用宽度,结合最小列宽计算出当前能容纳的整数列数,保证每行单元格数量一致
  • 单元格通过fixedSize(horizontal: false, vertical: true)让文本自动换行,避免截断;frame(maxWidth: .infinity)确保单元格填满列宽,解决自定义Layout出现的填充不充分问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 22:42:07