You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 17:25:52