SwiftUI复杂布局问题:选用哪种网格系统实现百分比分配?
SwiftUI 按百分比分配空间的网格布局方案选择
我写了下面这段SwiftUI代码:
struct MapPinSheetMoreInfoView: View { let item: Place var body: some View { HStack(alignment: .top, spacing: 3) { VStack(alignment: .leading) { Text("Location") .font(.caption.weight(.semibold)) .lineLimit(1) if item.address != "" { Text(item.address) .font(.footnote) .foregroundColor(.secondary) Text(item.city + ", " + item.state) .font(.footnote) .foregroundColor(.secondary) } else { Text("N/A") } } Divider() VStack(alignment: .leading) { HStack { VStack { Text("Weather") .font(.caption.weight(.semibold)) .lineLimit(1) HStack(alignment: .firstTextBaseline, spacing: 3) { Image(systemName: "waveform") .imageScale(.small) .foregroundColor(.secondary) Text("79%") .font(.caption.weight(.regular)) .foregroundColor(.secondary) } } Divider() HStack { Text("Hello, World!") } } Divider() HStack { HStack { Text("Hello, World!") } Divider() HStack { Text("Hello, World!") } } } } } }
当前代码的布局存在错位问题,我希望实现能让视图按固定百分比分配空间的网格布局,请问应该选用哪种网格系统?
推荐两种可行方案
1. 原生首选:Grid(iOS 16+、macOS 13+)
Grid是SwiftUI在iOS16之后推出的原生网格布局,支持通过gridCellColumns设置列宽比例,完全满足固定百分比分配的需求,代码简洁易维护。
修改后的示例代码:
struct MapPinSheetMoreInfoView: View { let item: Place var body: some View { Grid(alignment: .topLeading, horizontalSpacing: 3, verticalSpacing: 3) { GridRow { // 左侧区域:占总宽度1/3(对应30%左右比例) VStack(alignment: .leading) { Text("Location") .font(.caption.weight(.semibold)) .lineLimit(1) if item.address != "" { Text(item.address) .font(.footnote) .foregroundColor(.secondary) Text(item.city + ", " + item.state) .font(.footnote) .foregroundColor(.secondary) } else { Text("N/A") } } .gridCellColumns(1) Divider() // 右侧区域:占总宽度2/3(对应70%左右比例),内部再拆分为两列 Grid(alignment: .topLeading, horizontalSpacing: 3) { GridRow { VStack(alignment: .leading) { Text("Weather") .font(.caption.weight(.semibold)) .lineLimit(1) HStack(alignment: .firstTextBaseline, spacing: 3) { Image(systemName: "waveform") .imageScale(.small) .foregroundColor(.secondary) Text("79%") .font(.caption.weight(.regular)) .foregroundColor(.secondary) } } .gridCellColumns(1) Divider() Text("Hello, World!") .frame(maxWidth: .infinity, alignment: .leading) .gridCellColumns(1) } Divider() GridRow { Text("Hello, World!") .frame(maxWidth: .infinity, alignment: .leading) .gridCellColumns(1) Divider() Text("Hello, World!") .frame(maxWidth: .infinity, alignment: .leading) .gridCellColumns(1) } } .gridCellColumns(2) } } } }
2. 兼容旧系统:GeometryReader + 手动百分比宽度
如果需要支持iOS15及更早版本,可以用GeometryReader获取父视图的宽度,直接给子视图设置百分比宽度,实现固定比例分配。
示例代码:
struct MapPinSheetMoreInfoView: View { let item: Place var body: some View { GeometryReader { geo in HStack(alignment: .top, spacing: 3) { // 左侧占30%宽度 VStack(alignment: .leading) { Text("Location") .font(.caption.weight(.semibold)) .lineLimit(1) if item.address != "" { Text(item.address) .font(.footnote) .foregroundColor(.secondary) Text(item.city + ", " + item.state) .font(.footnote) .foregroundColor(.secondary) } else { Text("N/A") } } .frame(width: geo.size.width * 0.3) Divider() // 右侧占70%宽度 VStack(alignment: .leading, spacing: 3) { HStack(spacing: 3) { VStack(alignment: .leading) { Text("Weather") .font(.caption.weight(.semibold)) .lineLimit(1) HStack(alignment: .firstTextBaseline, spacing: 3) { Image(systemName: "waveform") .imageScale(.small) .foregroundColor(.secondary) Text("79%") .font(.caption.weight(.regular)) .foregroundColor(.secondary) } } .frame(width: (geo.size.width * 0.7 - 3) * 0.5) Divider() Text("Hello, World!") .frame(width: (geo.size.width * 0.7 - 3) * 0.5, alignment: .leading) } Divider() HStack(spacing: 3) { Text("Hello, World!") .frame(width: (geo.size.width * 0.7 - 3) * 0.5, alignment: .leading) Divider() Text("Hello, World!") .frame(width: (geo.size.width * 0.7 - 3) * 0.5, alignment: .leading) } } .frame(width: geo.size.width * 0.7) } } } }
方案对比
Grid:原生支持,代码结构清晰,自动适配不同屏幕,是iOS16+版本的最优选择。GeometryReader:兼容性强,能覆盖旧系统,但需要手动计算宽度,代码相对繁琐。
内容的提问来源于stack exchange,提问作者SLE
相关产品推荐
相关产品推荐

