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

如何在全iOS版本实现SwiftUI网格视图?解决元素左对齐问题

适配全iOS版本的SwiftUI双列网格实现方案

一、修复现有代码的布局问题

你当前代码的核心问题是固定元素宽度导致适配性差,以及内边距设置不合理,最后一行元素左对齐的问题可通过统一列宽、调整占位视图解决。修改后的代码如下:

import SwiftUI

struct NewViewPracc: View {
    var tempList: [String] = ["A", "B", "C", "D", "E"]
    // 统一定义间距与内边距
    private let columnSpacing: CGFloat = 16
    private let horizontalPadding: CGFloat = 16
    
    var body: some View {
        VStack {
            Text("Hacking with Swift")
                .padding()
                .background(Color.red)
                .cornerRadius(16)
                .overlay(
                    RoundedRectangle(cornerRadius: 32)
                        .stroke(Color.blue, lineWidth: 4)
                )
            
            ScrollView {
                // 通过GeometryReader动态计算列宽
                GeometryReader { geometry in
                    let columnWidth = (geometry.size.width - horizontalPadding * 2 - columnSpacing) / 2
                    
                    VStack(spacing: 4) {
                        ForEach(0..<numberOfRows, id: \.self) { rowIndex in
                            HStack(spacing: columnSpacing) {
                                ForEach(0..<2) { columnIndex in
                                    let index = rowIndex * 2 + columnIndex
                                    
                                    if index < tempList.count {
                                        GridItemView()
                                            .frame(width: columnWidth, height: 231)
                                    } else {
                                        // 用同宽空视图占位,保证最后一行布局对齐
                                        Color.clear.frame(width: columnWidth)
                                    }
                                }
                            }
                        }
                    }
                    .padding(.vertical, 16)
                }
            }
            .padding(.horizontal, horizontalPadding)
        }
    }
    
    // 简化行数计算逻辑
    var numberOfRows: Int {
        let itemCount = tempList.count
        let itemsPerRow = 2
        return (itemCount + itemsPerRow - 1) / itemsPerRow
    }
}

// 抽离网格元素为独立视图,提升复用性
struct GridItemView: View {
    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            Text("Header")
                .foregroundColor(Color.cyan)
                .frame(maxWidth: .infinity, alignment: .leading)
            
            Text("Title")
                .frame(maxWidth: .infinity, alignment: .leading)
            Text("sub title")
                .frame(maxWidth: .infinity, alignment: .leading)
        }
        .padding(.horizontal, 12)
        .padding(.top, 12)
        .padding(.bottom, 32)
        .background(Color.gray)
        .cornerRadius(16)
    }
}

struct NewViewPracc_Previews: PreviewProvider {
    static var previews: some View {
        NewViewPracc()
    }
}

修改核心要点:

  • 用GeometryReader动态计算列宽,适配不同屏幕尺寸
  • 给ScrollView设置统一左右内边距,替代原VStack的0内边距
  • 最后一行占位视图使用与真实元素相同的宽度,而非Spacer,保证布局对齐
  • 简化行数计算逻辑,避免冗余判断

二、通用全版本网格组件实现

如果需要更高复用性的网格(支持自定义列数、间距、内边距),可以封装兼容iOS 13+的通用网格视图:

import SwiftUI

struct UniversalGridView<Content: View>: View {
    let items: [Any]
    let columns: Int
    let columnSpacing: CGFloat
    let rowSpacing: CGFloat
    let horizontalPadding: CGFloat
    let content: (Any) -> Content
    
    init(
        items: [Any],
        columns: Int = 2,
        columnSpacing: CGFloat = 16,
        rowSpacing: CGFloat = 8,
        horizontalPadding: CGFloat = 16,
        @ViewBuilder content: @escaping (Any) -> Content
    ) {
        self.items = items
        self.columns = columns
        self.columnSpacing = columnSpacing
        self.rowSpacing = rowSpacing
        self.horizontalPadding = horizontalPadding
        self.content = content
    }
    
    var body: some View {
        ScrollView {
            GeometryReader { geometry in
                let columnWidth = (geometry.size.width - horizontalPadding * 2 - columnSpacing * CGFloat(columns - 1)) / CGFloat(columns)
                
                VStack(spacing: rowSpacing) {
                    ForEach(0..<numberOfRows, id: \.self) { rowIndex in
                        HStack(spacing: columnSpacing) {
                            ForEach(0..<columns) { columnIndex in
                                let index = rowIndex * columns + columnIndex
                                
                                if index < items.count {
                                    content(items[index])
                                        .frame(width: columnWidth)
                                } else {
                                    Color.clear.frame(width: columnWidth)
                                }
                            }
                        }
                    }
                }
                .padding(.vertical, 16)
            }
        }
        .padding(.horizontal, horizontalPadding)
    }
    
    private var numberOfRows: Int {
        (items.count + columns - 1) / columns
    }
}

// 使用示例
struct UniversalGridDemo: View {
    let tempList: [String] = ["A", "B", "C", "D", "E"]
    
    var body: some View {
        VStack {
            Text("Universal Grid Demo")
                .font(.title)
                .padding()
            
            UniversalGridView(items: tempList, columns: 2) { item in
                if let text = item as? String {
                    GridItemView(title: text)
                }
            }
        }
    }
}

struct GridItemView: View {
    let title: String
    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            Text("Header")
                .foregroundColor(.cyan)
                .frame(maxWidth: .infinity, alignment: .leading)
            
            Text(title)
                .font(.headline)
                .frame(maxWidth: .infinity, alignment: .leading)
            Text("sub title")
                .font(.caption)
                .frame(maxWidth: .infinity, alignment: .leading)
        }
        .padding(.horizontal, 12)
        .padding(.top, 12)
        .padding(.bottom, 32)
        .background(Color.gray.opacity(0.2))
        .cornerRadius(16)
        .frame(height: 231)
    }
}

通用组件优势:

  • 支持自定义列数、间距、内边距
  • 自动适配不同屏幕尺寸
  • 兼容iOS 13及以上版本
  • 视图内容可自定义,复用性强

内容的提问来源于stack exchange,提问作者Noob Developer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 18:53:13