在SwiftUI中创建每日时间轴日历

jv4diomz  于 2023-05-16  发布在  Swift
关注(0)|答案(2)|浏览(246)

bounty还有3天到期。回答此问题可获得+50声望奖励。markb希望引起更多关注这个问题。

我正在寻找关于如何构建SwiftUI每日日历列表的建议,因为我正在努力思考如何实现这一点。
这是我尝试创建的列表类型:image
我可以创建一个VStack来垂直循环一天中的小时数,然后再循环分割线--这看起来像苹果日历应用程序视图:blank list
我不明白的是如何将日历条目与时间对齐,然后如果是长事件,则跨越多个小时。
我从MSGraph接收带有startDate和endDate的事件数据。
我不需要拖动的事件的能力,只显示在时间槽。
我的目标是自己构建这一切,尽管我知道可能有一些软件包可以提供帮助。这只是应用程序的一小部分,当我只想有一个查看器时,我不想让日历功能过载。
这是我在哪里,但不知道如何将事件添加到列表中:

struct ContentView: View {
 var body: some View {
  ScrollView {
   VStack(alignment: .leading) {
    ForEach(8..<17) { index in
      Text("\(index)")
      Divider()
       .offset(y: -22)
       .padding(.leading, 30)
      Spacer()
     }
    }
   }
   .frame(maxWidth: .infinity, alignment: .leading)
   .padding()
  }
}
5lhxktic

5lhxktic1#

我会在小时网格上添加单个事件作为覆盖或在ZStack中。必须根据起始日期计算偏移。
下面是一个示例实现:

struct Event: Identifiable {
    let id = UUID()
    var startDate: Date
    var endDate: Date
    var title: String
}

struct ContentView: View {
    
    let date: Date = dateFrom(9, 5, 2023)
    
    let events: [Event] = [
        Event(startDate: dateFrom(9,5,2023,7,0), endDate: dateFrom(9,5,2023,8,0), title: "Event 1"),
        Event(startDate: dateFrom(9,5,2023,9,0), endDate: dateFrom(9,5,2023,10,0), title: "Event 2"),
        Event(startDate: dateFrom(9,5,2023,11,0), endDate: dateFrom(9,5,2023,12,00), title: "Event 3"),
        Event(startDate: dateFrom(9,5,2023,13,0), endDate: dateFrom(9,5,2023,14,45), title: "Event 4"),
        Event(startDate: dateFrom(9,5,2023,15,0), endDate: dateFrom(9,5,2023,15,45), title: "Event 5"),
    ]
    
    let hourHeight = 50.0
    
    var body: some View {
        VStack(alignment: .leading) {
            
            // Date headline
            HStack {
                Text(date.formatted(.dateTime.day().month()))
                    .bold()
                Text(date.formatted(.dateTime.year()))
            }
            .font(.title)
            Text(date.formatted(.dateTime.weekday(.wide)))
            
            ScrollView {
                ZStack(alignment: .topLeading) {
                    
                    VStack(alignment: .leading, spacing: 0) {
                        ForEach(7..<19) { hour in
                            HStack {
                                Text("\(hour)")
                                    .font(.caption)
                                    .frame(width: 20, alignment: .trailing)
                                Color.gray
                                    .frame(height: 1)
                            }
                            .frame(height: hourHeight)
                        }
                    }
                    
                    ForEach(events) { event in
                        eventCell(event)
                    }
                }
            }
        }
        .padding()
    }
    
    func eventCell(_ event: Event) -> some View {
        
        let duration = event.endDate.timeIntervalSince(event.startDate)
        let height = duration / 60 / 60 * hourHeight
        
        let calendar = Calendar.current
        let hour = calendar.component(.hour, from: event.startDate)
        let minute = calendar.component(.minute, from: event.startDate)
        let offset = Double(hour-7) * (hourHeight)
//                      + Double(minute)/60 ) * hourHeight
        
        print(hour, minute, Double(hour-7) + Double(minute)/60 )

        return VStack(alignment: .leading) {
            Text(event.startDate.formatted(.dateTime.hour().minute()))
            Text(event.title).bold()
        }
        .font(.caption)
        .frame(maxWidth: .infinity, alignment: .leading)
        .padding(4)
        .frame(height: height, alignment: .top)
        .background(
            RoundedRectangle(cornerRadius: 8)
                .fill(.teal).opacity(0.5)
        )
        .padding(.trailing, 30)
        .offset(x: 30, y: offset + 24)

    }
    
}

func dateFrom(_ day: Int, _ month: Int, _ year: Int, _ hour: Int = 0, _ minute: Int = 0) -> Date {
    let calendar = Calendar.current
    let dateComponents = DateComponents(year: year, month: month, day: day, hour: hour, minute: minute)
    return calendar.date(from: dateComponents) ?? .now
}
plupiseo

plupiseo2#

将您的事件时间与您可以使用DateComponentsForEach时间相匹配。

///Hours in a day - First ForEach
let hourlyComponents: [DateComponents] = (0...23).map { hour in
    DateComponents(hour: hour)
}

然后,您可以按小时对事件进行“分组”。

var eventsByHour: [DateComponents: [Event]]{
    Dictionary(grouping: events) { element in
        Calendar.current.dateComponents([.hour], from: element.startDate)
    }
}

现在要查找每小时的事件,您可以使用。

ForEach(eventsByHour[hourlyComponent, default: []], id:\.id){ event in

要调整它的大小,可以计算出elapsedHours,并使用类似于

.frame(height: height * event.elapsedHours)

如果事件是1.5小时,则大小将是高度的1.5倍。
至于调整分钟,如11:15,你可以得到小时相当的分钟,并把它变成小时,然后抵消。

.offset(y: height * event.startPosition)

15分钟是0.25小时。
我建议使用CalendarMeasurement来完成所有的数学运算。

let components = Calendar.current.dateComponents([.hour, .minute], from: max(startDate, midnightStart), to: min(endDate, midnightEnd))
let minToHour = Measurement(value: Double(components.minute ?? 0), unit: UnitDuration.minutes).converted(to: .hours)
let elapsedHours = Double(components.hour ?? 0) + minToHour.value

相关问题