简体   繁体   English

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

[英]Swift: Finding and plotting the hours in between two Dates

I am trying to create a UI like the below.我正在尝试创建如下所示的 UI。 I have a startDate and endDate (eg 1:55am and 11:35am).我有一个 startDate 和 endDate(例如上午 1:55 和上午 11:35)。 How can I proportionally (using Geometry reader) find each hour in between two dates and plot them as is done here?我怎样才能按比例(使用几何阅读器)找到两个日期和 plot 之间的每个小时,就像这里所做的那样? I am thinking at some point I need to use seconds or timeIntervalSince perhaps, but can't quite get my head around how best to go about it?我在想在某些时候我可能需要使用 seconds 或 timeIntervalSince,但我不太明白 go 的最佳方式是什么?

在此处输入图像描述

my code so far which gets the hours correctly, but I need to space them out proportionally according to their times:到目前为止,我的代码可以正确获取时间,但我需要根据时间按比例将它们分开:

What my code looks like:我的代码是什么样的: 在此处输入图像描述

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)
    }
}

There are a whole suite of methods in the Calendar class to help with what you're trying to do.日历 class 中有一整套方法可以帮助您完成尝试。

Off the top of my head, Id say you'd do something like the following:在我的脑海中,我说你会做如下的事情:

In a loop, use date(byAdding:to:wrappingComponents:) to add an hour at a time to your startDate.在一个循环中,使用date(byAdding:to:wrappingComponents:)一次将一个小时添加到您的 startDate。 If the result is ≤ endDate, use component(_:from:) to get the hour of the newly calculated date.如果结果 ≤ endDate,则使用component(_:from:)获取新计算日期的小时数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM