Swift:查找和绘制两个日期之间的小时数

问题描述

我正在尝试创建如下所示的 UI。我有一个 startDate 和 endDate(例如 1:55am 和 11:35am)。我怎样才能按比例(使用几何阅读器)找到两个日期之间的每小时并像这里所做的那样绘制它们?我在想在某个时候我可能需要使用 seconds 或 timeIntervalSince,但我不太明白如何最好地去做?

enter image description here

到目前为止,我的代码正确地获取了小时数,但我需要根据时间按比例将它们隔开:

我的代码是什么样的:

enter image description here

struct AsleepTimeView: View {
    
    @EnvironmentObject var dataStore: DataStore
    
    static let sleepTimeFormat: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateStyle = .none
        formatter.timeStyle = .short
        return formatter
    }()
    
    
    var body: some View {
        Color.clear.overlay(
            GeometryReader { geometry in
                if let mostRecentSleepDay = dataStore.pastSevenSleepDays?.last,let firstSleepSpan = mostRecentSleepDay.sleepSpans.first,let lastSleepSpan = mostRecentSleepDay.sleepSpans.last {
                    vstack(alignment: .center) {
                        HStack {
                            Text("\(firstSleepSpan.startDate,formatter: Self.sleepTimeFormat) ")
                                .font(.footnote)
                            Spacer()
                            Text("\(lastSleepSpan.endDate,formatter: Self.sleepTimeFormat)")
                                .font(.footnote)
                        }
                        HStack(spacing: 3) {
                            
                            ForEach(dataStore.sleepOrAwakeSpans) { sleepOrAwakeSpan in
                                
                                RoundedRectangle(cornerRadius: 5)
                                    .frame(width: getWidthForRoundedRectangle(proxy: geometry,spacing: 3,seconds: sleepOrAwakeSpan.seconds,sleepOrAwakeSpans: athlyticDataStore.sleepOrAwakeSpans),height: 10)
                                    
                                    .foregroundColor(sleepOrAwakeSpan.asleep == false ? TrackerConstants.scaleLevel5Color : TrackerConstants.scaleLevel8Color)
                                
                            }
                        }
                        HStack {
                            ForEach(getHoursBetweenTwoDates(startDate: firstSleepSpan.startDate,endDate: lastSleepSpan.endDate).map {  Calendar.current.component(.hour,from: $0) },id: \.self) { hour in
                                HStack {
                                    Text("\(hour)")
                                        .font(.footnote)
                                    Spacer()
                                }
                            }
                        }
                        HStack {
                            HStack {
                                Circle()
                                    .foregroundColor(TrackerConstants.scaleLevel8Color)
                                    .frame(width: 10,height: 10)
                                Text("Asleep")
                                    .font(.footnote)
                            }
                            HStack {
                                Circle()
                                    .foregroundColor(TrackerConstants.scaleLevel5Color)
                                    .frame(width: 10,height: 10)
                                Text("Awake")
                                    .font(.footnote)
                            }
                            Spacer()
                        }
                    }
                }
 
            })     // end of overlay
    }
    
    //helper
    private func getWidthForRoundedRectangle(proxy: GeometryProxy,spacing: Int,seconds: TimeInterval,sleepOrAwakeSpans: [SleepOrAwakeSpan]) -> CGFloat {
        
        let totalSpace = (sleepOrAwakeSpans.count - 1) * spacing
        let totalSleepTime = sleepOrAwakeSpans.map { $0.endTime.timeIntervalSince($0.startTime) }.reduce(0,+)
        
        guard totalSleepTime > 0 else { return 0}
        
        let width =  (proxy.size.width - CGFloat(totalSpace)) * CGFloat(seconds / totalSleepTime)
        return width
    }
    
    func datesRange(from: Date,to: Date,component: Calendar.Component) -> [Date] {
        // in case of the "from" date is more than "to" date,// it should returns an empty array:
        if from > to { return [Date]() }
        
        var tempDate = from
        var array = [tempDate]
        
        while tempDate < to {
            tempDate = Calendar.current.date(byAdding: component,value: 1,to: tempDate)!
            array.append(tempDate)
        }
        
        return array
    }
    
    func getHoursBetweenTwoDates(startDate: Date,endDate: Date) -> [Date] {
        
        var finalArrayOfHours = [Date]()
        
        guard endDate > startDate else { return finalArrayOfHours }
        
        let arrayOfHours = datesRange(from: startDate,to: endDate,component: .hour)
        
        for date in arrayOfHours {
            let hour = date.nearestHour()
            finalArrayOfHours.append(hour)
        }
        
        return finalArrayOfHours
        
    }
    
}

extension Date {
    func nearestHour() -> Date {
        return Date(timeIntervalSinceReferenceDate:
                        (timeIntervalSinceReferenceDate / 3600.0).rounded(.toNearestOrEven) * 3600.0)
    }
}

解决方法

Calendar 类中有一整套方法可以帮助您完成任务。

在我的脑海里,我想说你会做如下的事情:

在循环中,使用 use Illuminate\Support\Facades\Route; use App\Http\Controllers\Posts\VuePostController; Route::get('posts',[VuePostController::class,'index']); Route::get('post/{id}','show']); Route::post('post','store']); Route::put('post','store']); Route::delete('post/{id}','destroy']); 将一个小时一次添加到您的 startDate。如果结果≤ endDate,则使用date(byAdding:to:wrappingComponents:) 获取新计算日期的小时。