如何实现iOS日历App样式的时间轴(含当前时间红线)?
SwiftUI 日历时间轴:添加当前时间红线的实现方案
我是SwiftUI新手,正在尝试制作类似iOS日历App的时间轴,要求左侧显示小时、每小时设置分隔线,并添加标记当前时间的红线。目前已完成小时与分隔线的实现,但卡在根据时间和间距正确绘制并定位当前时间红线,恳请提供帮助或指引。
现有代码
struct ContentView: View { let dateFormatter = DateFormatter() init(){ dateFormatter.dateFormat = "HH:mm" } var sortedTimeFrom: [Date] = { let today = Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: Date())! var sortedTime: [Date] = [] var calender = Calendar(identifier: .iso8601) calender.locale = Locale(identifier: "en_US_POSIX") let currentHour = calender.component(.hour, from: today) (0...(24)).forEach { guard let newDate = calender.date(byAdding: .hour, value: $0, to: today), calender.component(.hour, from: newDate) <= 23 else { return } sortedTime.append(newDate) } return sortedTime }() var body: some View { ScrollView(.vertical) { ForEach(sortedTimeFrom, id: \.self) { date in VStack(alignment: .leading){ HStack { Spacer() Text(date, formatter: dateFormatter) VStack{ Color.gray.frame(height: 1 / UIScreen.main.scale) } } }.frame(maxWidth: .infinity, alignment: .leading) } } } }
修改后的实现代码
struct ContentView: View { let dateFormatter = DateFormatter() // 定义每小时高度,可根据UI需求调整 private let hourHeight: CGFloat = 60 // 实时更新的当前时间 @State private var currentTime: Date = Date() init(){ dateFormatter.dateFormat = "HH:mm" } var sortedTimeFrom: [Date] = { let today = Calendar.current.date(bySettingHour: 0, minute: 0, second: 0, of: Date())! var sortedTime: [Date] = [] let calendar = Calendar(identifier: .iso8601) calendar.locale = Locale(identifier: "en_US_POSIX") for hour in 0...23 { guard let newDate = calendar.date(byAdding: .hour, value: hour, to: today) else { continue } sortedTime.append(newDate) } return sortedTime }() // 计算当前时间相对于0点的垂直偏移量 private var currentTimeOffset: CGFloat { let calendar = Calendar.current let totalMinutes = calendar.component(.minute, from: currentTime) + calendar.component(.hour, from: currentTime) * 60 return CGFloat(totalMinutes) * (hourHeight / 60) } var body: some View { ZStack(alignment: .leading) { ScrollView(.vertical) { VStack(spacing: 0) { ForEach(sortedTimeFrom, id: \.self) { date in HStack(alignment: .top, spacing: 16) { // 固定小时文本宽度,保证对齐 Text(date, formatter: dateFormatter) .frame(width: 60, alignment: .trailing) // 分隔线占满剩余宽度 Color.gray .frame(height: 1 / UIScreen.main.scale) .frame(maxWidth: .infinity) } .frame(height: hourHeight) // 每个小时块固定高度 } } } // 当前时间红线 Color.red .frame(width: 1) .frame(maxHeight: .infinity) // 偏移到分隔线位置:小时宽度60 + 间距16 .offset(x: 76) // 根据当前时间计算垂直偏移 .offset(y: currentTimeOffset) .ignoresSafeArea() // 可选添加时间标记圆点 Circle() .fill(Color.red) .frame(width: 8, height: 8) .offset(x: 72, y: currentTimeOffset) } .onAppear { // 每秒更新当前时间,让红线实时同步 Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in currentTime = Date() } } } }
关键实现说明
- 固定小时块高度:设置
hourHeight统一每个小时区域的高度,为偏移计算提供基准 - 时间偏移计算:通过
currentTimeOffset将当前时间的总分钟数转换成垂直偏移量,确保红线精准对应当前时间点 - 层级布局:用
ZStack将红线置于时间轴上方,避免被滚动内容遮挡 - 实时更新:通过Timer每秒刷新当前时间,让红线随时间动态移动
- 布局对齐:固定小时文本宽度,调整偏移量让红线与分隔线精准对齐
内容的提问来源于stack exchange,提问作者Mittchel
相关产品推荐
相关产品推荐

