SwiftUI如何实现LazyVGrid行内元素填充可用空间等高布局
6月14日更新
我目前摸索出了一种拼凑式的临时实现方案,但并不理想:
借助如下Array扩展,可将元素数组按指定大小分块,例如每2个元素为一组:
extension Array { func chunked(into size: Int) -> [[Element]] { return stride(from: 0, to: count, by: size).map { Array(self[$0 ..< Swift.min($0 + size, count)]) } } }
将数组拆分为指定大小的子数组后,我通过VStack嵌套多层HStack的方式实现布局,代码如下:
var body: some View { VStack { ForEach(itemText.chunked(into: 2), id: \.self) { textCouple in HStack { ForEach(textCouple, id: \.self) { text in item(text: text) .frame(width: 150) } } } .fixedSize(horizontal: false, vertical: true) } }
这种临时方案扩展性较差,我仍希望找到基于LazyVGrid的原生实现方案。从iOS14系统开始,LazyVGrid行内的视图默认不再自动扩展填充可用空间。
问题场景
我有一组视图数组,每个视图都包含独立的、可换行展示的多行文本内容。我需要实现的效果是:LazyVGrid每一行的高度由该行内高度最大的元素决定,同一行内的其余元素自动扩展,拉伸至与该行最高元素保持一致高度。
以下是我的项目核心简化代码:
struct SwiftUIView: View { let resultGridLayout = [GridItem(.adaptive(minimum: 160), spacing: 10, alignment: .top)] let itemText = [ "Test 1", "Testung 122kdjaslkdjakldjklasdmasd", "Teslksjf", "Telksj;flkjasf;lkjasflkja;slfkjas;lkfjaslkfjaskfjas;kfj;askfj;askfj;aslkfj;askfj;aslkfj;asklfj", "Test3", "`kjdnlaskdnaskljndjkasndkjasndajksndla" ] var body: some View { LazyVGrid(columns: resultGridLayout, spacing: 10) { ForEach(itemText, id: \.self) { text in item(text: text) .frame(maxHeight: .infinity) } } } func item(text: String) -> some View { Text(text) .padding() .background(Color.blue.opacity(0.3)) .cornerRadius(10) } }
当前运行效果为文本内容较短的视图并未自动扩展填充空间,不符合预期。我期望实现的效果为每一行的高度可根据该行内最高视图自适应,不同行高度可以不同,但同一行内所有元素高度保持一致。
我无法采用将所有网格元素适配为全局最高元素高度的方案:网格内元素量级很大,若因单个极高元素导致所有元素被强制拉齐高度,会造成不必要的空间浪费。
原生实现方案
不需要手动拆分数组嵌套栈布局,直接调整LazyVGrid配置即可实现需求,核心修改点共2项:
- 将
GridItem初始化方法中的alignment参数从.top修改为.fill,该模式下网格会自动将同一行的所有单元格高度拉伸到与行内最高单元格一致 - 若需要单元格内的内容保持顶部对齐(而非默认垂直居中),在单元格内容上设置
.frame(maxHeight: .infinity, alignment: .top)即可
修改后的可运行核心代码如下:
struct SwiftUIView: View { // 调整GridItem对齐模式为.fill let resultGridLayout = [GridItem(.adaptive(minimum: 160), spacing: 10, alignment: .fill)] let itemText = [ "Test 1", "Testung 122kdjaslkdjakldjklasdmasd", "Teslksjf", "Telksj;flkjasf;lkjasflkja;slfkjas;lkfjaslkfjaskfjas;kfj;askfj;askfj;aslkfj;askfj;aslkfj;asklfj", "Test3", "`kjdnlaskdnaskljndjkasndkjasndajksndla" ] var body: some View { LazyVGrid(columns: resultGridLayout, spacing: 10) { ForEach(itemText, id: \.self) { text in item(text: text) .frame(maxHeight: .infinity, alignment: .top) } } .padding() } func item(text: String) -> some View { Text(text) .padding() .frame(maxWidth: .infinity, alignment: .leading) .background(Color.blue.opacity(0.3)) .cornerRadius(10) } }
该方案的优势:
- 完全基于系统原生LazyVGrid实现,自适应列数、滚动复用等逻辑都由系统原生框架保障,不需要手动维护数组分块逻辑,扩展性更强
- 行高按单行列内最高元素独立计算,不会出现单个超长内容拉高所有网格行高的问题,不会造成空间浪费
- 兼容iOS14及以上所有系统版本,无需依赖高版本独有API
内容的提问来源于stack exchange,提问作者DevB1
相关产品推荐
相关产品推荐

