iOS 14中如何让ScrollView内自适应列LazyVGrid内容水平居中?
iOS14 中实现 LazyVGrid 内容少时水平居中的方案
你当前代码里,LazyVGrid 使用 .adaptive 列布局时,内容较少会左对齐,核心原因是网格被强制设为占满容器宽度,导致单行内的 item 无法居中。以下是两种适配 iOS14 的解决方式:
方案一:根据内容数量动态切换布局(简单直观)
先计算容器内一行能容纳的最大 item 数量,当总 item 数 ≤ 该数量时,用 HStack 包裹并居中显示;超过时则沿用原 LazyVGrid 布局,逻辑清晰易维护。
import SwiftUI struct MyView: View { var numbers = 1...30 private let itemSize: CGFloat = 50 private let gridSpacing: CGFloat = 5 var body: some View { VStack { GeometryReader { geometry in ScrollView { // 计算容器可用宽度(减去默认左右padding的16*2) let availableWidth = geometry.size.width - 32 // 计算一行最多能放的item数量 let maxItemsPerRow = Int((availableWidth + gridSpacing) / (itemSize + gridSpacing)) if numbers.count <= maxItemsPerRow { // 内容不足一行,用HStack居中展示 HStack(spacing: gridSpacing) { ForEach(numbers, id: \.self) { i in Text(String(i)) .frame(width: itemSize, height: itemSize) .border(.blue) } } .border(.red) .padding() .frame(minHeight: geometry.size.height) .frame(maxWidth: .infinity, alignment: .center) } else { // 内容超过一行,使用自适应列的LazyVGrid let columns = [ GridItem(.adaptive(minimum: itemSize), spacing: gridSpacing) ] LazyVGrid(columns: columns, spacing: gridSpacing) { ForEach(numbers, id: \.self) { i in Text(String(i)) .frame(width: itemSize, height: itemSize) .border(.blue) } } .border(.red) .padding() .frame(minWidth: geometry.size.width, minHeight: geometry.size.height) } } } }.frame(width: 500, height: 200) } } import PlaygroundSupport PlaygroundPage.current.setLiveView(MyView())
方案二:使用 PreferenceKey 获取内容宽度(通用适配)
通过自定义 PreferenceKey 捕获 LazyVGrid 的实际内容宽度,动态调整网格的显示宽度:当内容宽度小于容器宽度时居中,否则占满容器,适合需要保持 LazyVGrid 布局结构的复杂场景。
import SwiftUI // 自定义PreferenceKey,用于捕获网格内容的最大宽度 struct GridContentWidthKey: PreferenceKey { static var defaultValue: CGFloat = 0 static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { value = max(value, nextValue()) } } struct MyView: View { var numbers = 1...30 private let itemSize: CGFloat = 50 private let gridSpacing: CGFloat = 5 @State private var gridContentWidth: CGFloat = 0 var body: some View { VStack { GeometryReader { geometry in ScrollView { let columns = [ GridItem(.adaptive(minimum: itemSize), spacing: gridSpacing) ] LazyVGrid(columns: columns, spacing: gridSpacing) { ForEach(numbers, id: \.self) { i in Text(String(i)) .frame(width: itemSize, height: itemSize) .border(.blue) // 捕获每个item的位置,计算网格内容的最大宽度 .background( GeometryReader { proxy in Color.clear .preference(key: GridContentWidthKey.self, value: proxy.frame(in: .local).maxX) } ) } } .border(.red) .padding() // 更新捕获到的网格内容宽度 .onPreferenceChange(GridContentWidthKey.self) { newWidth in gridContentWidth = newWidth } // 动态设置网格宽度:内容宽度小于容器可用宽度时用内容宽度,否则占满容器 .frame(width: min(gridContentWidth + gridSpacing, geometry.size.width - 32) + 32) .frame(minHeight: geometry.size.height) .frame(maxWidth: .infinity, alignment: .center) } } }.frame(width: 500, height: 200) } } import PlaygroundSupport PlaygroundPage.current.setLiveView(MyView())
内容的提问来源于stack exchange,提问作者Lysann Tranvouez
相关产品推荐
相关产品推荐

