3

我试图在滚动日历时获取当前月份。我的目标是拥有当前月份的标题,然后当视图向下滚动到新月份时,标题会更新为相应的月份。我尝试使用 LazyVStack 并在 onAppear 方法中设置当前月份,但使用lazyVStack 从来都不是一致的,并且总是关闭几个月。下面的代码针对 iPad 日历月视图进行了优化。

import SwiftUI

fileprivate extension DateFormatter {
    static var month: DateFormatter {
        let formatter = DateFormatter()
        formatter.dateFormat = "MMMM"
        return formatter
    }

    static var monthAndYear: DateFormatter {
        let formatter = DateFormatter()
        formatter.dateFormat = "MMMM yyyy"
        return formatter
    }
}

fileprivate extension Calendar {
    func generateDates(
        inside interval: DateInterval,
        matching components: DateComponents
    ) -> [Date] {
        var dates: [Date] = []
        dates.append(interval.start)

        enumerateDates(
            startingAfter: interval.start,
            matching: components,
            matchingPolicy: .nextTime
        ) { date, _, stop in
            if let date = date {
                if date < interval.end {
                    dates.append(date)
                } else {
                    stop = true
                }
            }
        }

        return dates
    }
}

struct WeekView<DateView>: View where DateView: View {
    @Environment(\.calendar) var calendar

    let week: Date
    let content: (Date) -> DateView

    init(week: Date, @ViewBuilder content: @escaping (Date) -> DateView) {
        self.week = week
        self.content = content
    }

    private var days: [Date] {
        guard
            let weekInterval = calendar.dateInterval(of: .weekOfYear, for: week)
            else { return [] }
        return calendar.generateDates(
            inside: weekInterval,
            matching: DateComponents(hour: 0, minute: 0, second: 0)
        )
    }
    
    func dateInWeekened(date: Date) -> Bool {
        var bool = false
        if Calendar.current.isDateInWeekend(date) {
           bool = true
        }
        return bool
    }
    

    var body: some View {
        HStack(spacing: 0.0) {
           
            ForEach(days, id: \.self) { date in
                HStack(alignment: .top, spacing: 0.0) {
                    Divider()
                    VStack(alignment: .trailing, spacing: 0.0){
                    if self.calendar.isDate(self.week, equalTo: date, toGranularity: .month) {
                        HStack{
                            Spacer()
                            self.content(date)
                                .foregroundColor(dateInWeekened(date: date) == true ? .secondary : .primary)
                                .padding(8)
                        }
                        
                    } else {
                        HStack{
                            Spacer()
                            self.content(date)
                                .hidden()
                               
                        }
                    }
                        Spacer()
                    }
                }
                .frame(height: UIScreen.screenHeight / 7)
                .background(dateInWeekened(date: date) == true ? Color("CalendarWeekenedColor") : Color(.systemBackground))
                .edgesIgnoringSafeArea(.all)
            }
          
        }
       
    }
}

struct MonthView<DateView>: View where DateView: View {
    @Environment(\.calendar) var calendar

    let month: Date
    let showHeader: Bool
    let content: (Date) -> DateView

    init(
        month: Date,
        showHeader: Bool = true,
        @ViewBuilder content: @escaping (Date) -> DateView
    ) {
        self.month = month
        self.content = content
        self.showHeader = showHeader
    }

    private var weeks: [Date] {
        guard
            let monthInterval = calendar.dateInterval(of: .month, for: month)
            else { return [] }
        return calendar.generateDates(
            inside: monthInterval,
            matching: DateComponents(hour: 0, minute: 0, second: 0, weekday: calendar.firstWeekday)
        )
    }

    private var header: some View {
        let component = calendar.component(.month, from: month)
        let formatter = component == 1 ? DateFormatter.monthAndYear : .month
        return
            HStack{Text(formatter.string(from: month))
            .font(.title)
            .padding()
                Spacer()
            }.padding(.leading)
    }

    var body: some View {
        VStack(spacing: 0.0) {
           if showHeader {
               header
           }
           else {
            ForEach(weeks, id: \.self) { week in
                WeekView(week: week, content: self.content)
                    .edgesIgnoringSafeArea(.all)
                Divider()
                    .edgesIgnoringSafeArea(.all)
            }
           }
        }
    }
}

struct CalendarView<DateView>: View where DateView: View {
    @Environment(\.calendar) var calendar

    let interval: DateInterval
    let content: (Date) -> DateView
    @Binding var curentDate: Date
    @State var position = 0

    init(interval: DateInterval, @ViewBuilder content: @escaping (Date) -> DateView, currentDate: Binding<Date>) {
        self.interval = interval
        self.content = content
        self._curentDate = currentDate
    }

    private var months: [Date] {
        calendar.generateDates(
            inside: interval,
            matching: DateComponents(day: 1, hour: 0, minute: 0, second: 0)
        )
    }
    
    var body: some View {
      
            ScrollView(.vertical, showsIndicators: false) {
                
            LazyVStack(spacing: 0.0){
                ForEach(months, id: \.self) { month in
                    MonthView(month: month, showHeader: false, content: self.content)
                        .edgesIgnoringSafeArea(.all)
                        .frame(maxHeight: .infinity)
                        .onAppear {
                            let component = calendar.component(.month, from: month)
                            let formatter = component == 1 ? DateFormatter.monthAndYear : .month
                            print("Current Month is \(formatter.string(from: month))")
                        }
                }
            }
            
 
        }
            
        
    }
}

struct CalendarMonthView: View {
    
    @Environment(\.calendar) var calendar
    @Binding var currentDate: Date
  
    private var year: DateInterval {
        calendar.dateInterval(of: .year, for: Date())!
    }

    var body: some View {
        CalendarView(interval: year, content: { date in
            Text("30")
                .hidden()
                .overlay(
                    Text(String(self.calendar.component(.day, from: date)))
                )
        }, currentDate: $currentDate)
    
        .background(Color("NavigationBarColor"))
        .edgesIgnoringSafeArea(.all)
    }
}
4

0 回答 0