SwiftUI:如何对齐HStack内银行营业时间并修复多时段间距问题
问题描述
我有一个SwiftUI视图用来展示银行营业时间,日期在左侧、时段在右侧,功能正常但存在问题:单个日期对应多个时段时,第二个时段会和下一个日期的内容错位。需求是同一日期的多个时段之间无间距,仅不同日期的内容之间添加间距。
原代码
struct OpeningHours: View { let titles: [String] let hours: [String] init(titles: [String], hours: [String]) { self.titles = titles self.hours = hours } var body: some View { HStack(alignment: .top, spacing: .zero) { VStack(alignment: .leading,spacing: .zero) { ForEach(titles, id: \.self) { title in Text(title) .rchFont(.rchBody) .fixedSize(horizontal: false, vertical: true) .multilineTextAlignment(.leading) .padding(.trailing, .md) .padding(.bottom, RCHPadding.xs.size + RCHPadding.sm.size) } } Spacer() VStack(alignment: .trailing, spacing: .zero) { ForEach(hours, id: \.self) { hour in Text(hour) .fixedSize(horizontal: false, vertical: true) .multilineTextAlignment(.trailing) .rchFont(.rchBodyBold) .padding(.bottom, RCHPadding.xs.size + RCHPadding.sm.size) } } } .padding(.top, .xs) } }
数据源示例
"specialOpeningTimes": [ { "openingTimeType": "ONE_DATE", "fromDate": "2023-10-09", "toDate": null, "timeRanges": [ { "fromTime": "09:00", "toTime": "12:00" }, { "fromTime": "09:00", "toTime": "12:00" } ] } ]
解决方案
问题根源在于原视图将日期和时段拆分为两个独立的VStack,当单个日期对应多个时段时,左右条目数量不匹配,叠加的padding导致错位。我们需要重构数据结构和视图层级来解决:
1. 定义分组数据模型
先把日期和对应的多个时段封装成一个模型,确保数据结构和展示逻辑匹配:
// 对应数据源的解析模型 struct SpecialOpeningTime: Decodable { let openingTimeType: String let fromDate: String let toDate: String? let timeRanges: [TimeRange] } struct TimeRange: Decodable { let fromTime: String let toTime: String } // 用于视图展示的分组模型 struct OpeningTimeGroup: Identifiable { let id = UUID() let dateTitle: String let timeRanges: [String] }
2. 修改OpeningHours视图
调整视图层级,按日期分组展示,同一日期的时段无间距,不同日期组之间添加间距:
struct OpeningHours: View { let timeGroups: [OpeningTimeGroup] var body: some View { // 外层VStack控制不同日期组之间的间距 VStack(spacing: RCHPadding.xs.size + RCHPadding.sm.size) { ForEach(timeGroups) { group in HStack(alignment: .top, spacing: .zero) { // 左侧日期:每个组仅显示一次 Text(group.dateTitle) .rchFont(.rchBody) .fixedSize(horizontal: false, vertical: true) .multilineTextAlignment(.leading) .padding(.trailing, .md) Spacer() // 右侧时段列表:同一日期的时段之间无间距 VStack(alignment: .trailing, spacing: .zero) { ForEach(group.timeRanges, id: \.self) { time in Text(time) .fixedSize(horizontal: false, vertical: true) .multilineTextAlignment(.trailing) .rchFont(.rchBodyBold) } } } } } .padding(.top, .xs) } }
3. 数据源转换
将原始JSON数据转换为视图所需的分组模型:
// 假设已解析得到specialOpeningTimes数组 let specialTimes: [SpecialOpeningTime] = ... let timeGroups = specialTimes.map { time in // 可根据需求格式化日期字符串,比如转为"10月09日" let dateTitle = time.fromDate // 把每个时段转为字符串格式 let timeStrings = time.timeRanges.map { range in "\(range.fromTime)-\(range.toTime)" } return OpeningTimeGroup(dateTitle: dateTitle, timeRanges: timeStrings) } // 使用视图 OpeningHours(timeGroups: timeGroups)
思路说明
- 数据驱动视图:通过分组模型确保每个日期和其对应的时段绑定,避免左右视图条目数量不一致的问题。
- 分层控制间距:外层VStack负责不同日期之间的间距,内部VStack负责同一日期时段的无间距展示,精准控制间距位置。
- 移除冗余padding:把原单个Text的bottom padding转移到组与组之间,避免不必要的空隙叠加。
内容的提问来源于stack exchange,提问作者user20273483
相关产品推荐
相关产品推荐

