ScrollView SwiftUI

问题描述

我用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,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
    }
}

解决方法

这个库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)
                    }
                }
            }
        }
    }

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...