1

我用 SwiftUI 构建了一个简单的水平日历,它运行良好。但我希望它在应用启动时自动滚动到当前日期。我怎样才能做到这一点?我假设我应该使用 GeometryReader 来获取当前日期的框架位置并基于它设置偏移量,但 ScrollView 没有内容偏移量修饰符。我知道在 iOS 14 中我们现在有 ScrollViewReader 但 iOS 13 呢?

struct MainCalendar: View {
    @Environment(\.calendar) var calendar
    
    @State private var month = Date()
    @State private var selectedDate: Date = Date()
    
    var body: some View {
        VStack(spacing: 30) {
            MonthAndYearView(date: $month)
            
            WeekDaysView(date: month)
        }
        .padding(.vertical)
    }
}

struct MonthAndYearView: View  {
    @Environment(\.calendar) var calendar
    private let formatter = DateFormatter.monthAndYear
    
    @Binding var date: Date
    
    var body: some View {
        HStack(spacing: 20) {
            Button(action: {
                self.date = calendar.date(byAdding: .month, value: -1, to: date)!
            }, label: {
                Image(systemName: "chevron.left")
            })
            
            Text(formatter.string(from: date))
                .font(.system(size: 18, weight: .semibold))
            
            Button(action: {
                self.date = calendar.date(byAdding: .month, value: 1, to: date)!
            }, label: {
                Image(systemName: "chevron.right")
            })
        }
    }
}

struct WeekDaysView: View {
    @Environment(\.calendar) var calenar
    
    let date: Date
    
    var body: some View {
        ScrollView(.horizontal, showsIndicators: false) {
            HStack(spacing: 30) {
                ForEach(days, id: \.self) { day in
                    VStack(spacing: 20) {
                        
                        Text(daySymbol(date: day))
                            .font(.system(size: 14, weight: .regular))
                        
                        if calenar.isDateInToday(day) {
                            Text("\(dayNumber(date: day))")
                                .foregroundColor(Color.white)
                                .background(Circle().foregroundColor(.blue)
                                                .frame(width: 40, height: 40))
                        } else {
                            Text("\(dayNumber(date: day))")
                        }
                    }
                }
            }
            .padding([.horizontal])
            .padding(.bottom, 15)
        }
    }
    
    private func dayNumber(date: Date) -> String {
        let formatter = DateFormatter.dayNumber
        let dayNumber = formatter.string(from: date)
        return dayNumber
    }
    
    private var days: [Date] {
        guard let interval = calenar.dateInterval(of: .month, for: date) else { return [] }
        return calenar.generateDates(inside: interval, matching: DateComponents(hour: 0, minute: 0, second: 0))
    }
    
    private func daySymbol(date: Date) -> String {
        let dayFormatter = DateFormatter.weekDay
        let weekDay = dayFormatter.string(from: date)
        return weekDay
    }
}
4

1 回答 1

1

这个库https://github.com/Amzd/ScrollViewProxy解决了我的问题。

struct WeekDaysView: View {
    let date: Date
    
    @Environment(\.calendar) var calendar
    @State private var scrollTarget: Int? = nil
    
    var body: some View {
        ScrollViewReader { proxy in
            ScrollView(.horizontal, showsIndicators: false) {
                HStack(spacing: 30) {
                    ForEach(Array(zip(days.indices, days)), id: \.0) { index, day in
                        VStack(spacing: 20) {
                            
                            Text(daySymbol(date: day))
                                .font(.system(size: 14, weight: .regular))
                                .scrollId(index)
                            
                            if calendar.isDateInToday(day) {
                                Text("\(dayNumber(date: day))")
                                    .foregroundColor(Color.white)
                                    .background(Circle().foregroundColor(.blue)
                                                    .frame(width: 40, height: 40))
                                    .onAppear {
                                        scrollTarget = index
                                    }
                                
                            } else {
                                Text("\(dayNumber(date: day))")
                            }
                        }
                    }
                }
                .padding([.horizontal])
                .padding(.bottom, 15)
            }
            .onAppear {
                DispatchQueue.main.async {
                    withAnimation {
                        proxy.scrollTo(scrollTarget, alignment: .center, animated: true)
                    }
                }
            }
        }
    }
于 2020-10-23T17:40:08.817 回答