如果以编程方式设置选择,则不会调用 MultiDatePicker onChange

ste*_*CDP 2 ios swift swiftui

我尝试通过按钮以编程方式在 MultiDatePicker 中选择日期(今天)。选择按预期进行,并且将调用 onChange 修饰符,并且今天在选取器中标记为已选择。但是,当我尝试直接在 MultiDatePicker 中取消选择日期时,日期不再被标记,但 onChange 修饰符将不会被调用。如果您现在点击 MultiDatePicker 中的另一个日期,则两个日期、另一个日期和今天的日期都将标记为已选择。

import SwiftUI

struct ContentView: View {

    @Environment(\.calendar) private var calendar

    @State private var selectedDates: Set<DateComponents> = []

    @State private var onChangeCounter = 0
    
    var body: some View {
        VStack {
            MultiDatePicker("Select dates", selection: $selectedDates)
                .frame(height: UIScreen.main.bounds.width)
                .onChange(of: selectedDates) { _ in
                    self.onChangeCounter += 1
                }

            Button("Select today") {
                let todayDatecomponents = calendar.dateComponents(in: calendar.timeZone, from: Date.now)
                selectedDates.insert(todayDatecomponents)
            }
            .foregroundColor(.white)
            .frame(minWidth: 150)
            .padding()
            .background(Color.accentColor)
            .cornerRadius(20)

            HStack {
                Text("onChangeCounter")
                Spacer()
                Text(String(onChangeCounter))
            }
            .padding()

            Spacer()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Run Code Online (Sandbox Code Playgroud)

iPhone 屏幕视频

我是否做错了什么,或者是否无法以编程方式在 MultiDatePicker 中选择日期?

谢谢你!

Don*_*Mag 5

就本次讨论而言,今天是指2022 年 12 月 17 日

问题是Date.now不等于今天

我在美国东海岸时区...如果我添加一个按钮print(Date.now)并点击它,我会在调试控制台中看到以下内容:

2022-12-17 14:08:52 +0000
Run Code Online (Sandbox Code Playgroud)

如果我 4 秒后再次点击它,我会看到以下内容:

2022-12-17 14:08:56 +0000
Run Code Online (Sandbox Code Playgroud)

这两个日期相等。

那么,让我们看看MultiDatePicker它的用途是什么selection

将你的更改MultiDatePicker为:

        MultiDatePicker("Select dates", selection: $selectedDates)
            .frame(height: UIScreen.main.bounds.width)
            .onChange(of: selectedDates) { _ in
                print("onChange")
                selectedDates.forEach { d in
                    print(d)
                }
                print()
                self.onChangeCounter += 1
            }
Run Code Online (Sandbox Code Playgroud)

如果我运行该应用程序并选择 12 月 14 日、19 日和 8 日,我会看到以下内容:

onChange
calendar: gregorian (current) era: 1 year: 2022 month: 12 day: 14 isLeapMonth: false 

onChange
calendar: gregorian (current) era: 1 year: 2022 month: 12 day: 14 isLeapMonth: false 
calendar: gregorian (current) era: 1 year: 2022 month: 12 day: 19 isLeapMonth: false 

onChange
calendar: gregorian (current) era: 1 year: 2022 month: 12 day: 14 isLeapMonth: false 
calendar: gregorian (current) era: 1 year: 2022 month: 12 day: 19 isLeapMonth: false 
calendar: gregorian (current) era: 1 year: 2022 month: 12 day: 8 isLeapMonth: false 
Run Code Online (Sandbox Code Playgroud)

现在,我取消选择第 19 个,然后我看到:

onChange
calendar: gregorian (current) era: 1 year: 2022 month: 12 day: 14 isLeapMonth: false 
calendar: gregorian (current) era: 1 year: 2022 month: 12 day: 8 isLeapMonth: false 
Run Code Online (Sandbox Code Playgroud)

19号已正确从 中删除Set

现在,我点击“选择今天”按钮,我看到了:

onChange
calendar: gregorian (current) era: 1 year: 2022 month: 12 day: 14 isLeapMonth: false 
calendar: gregorian (current) era: 1 year: 2022 month: 12 day: 8 isLeapMonth: false 
calendar: gregorian (current) timeZone: America/New_York (fixed (equal to current)) era: 1 year: 2022 month: 12 day: 17 hour: 9 minute: 24 second: 11 nanosecond: 339460015 weekday: 7 weekdayOrdinal: 3 quarter: 0 weekOfMonth: 3 weekOfYear: 51 yearForWeekOfYear: 2022 isLeapMonth: false 
Run Code Online (Sandbox Code Playgroud)

正如我们所看到的,这两行:

let todayDatecomponents = calendar.dateComponents(in: calendar.timeZone, from: Date.now)
selectedDates.insert(todayDatecomponents)
Run Code Online (Sandbox Code Playgroud)

插入一个包含更多DateComponents细节的对象。

如果我再次点击“选择今天”,我会得到以下信息:

onChange
calendar: gregorian (current) era: 1 year: 2022 month: 12 day: 8 isLeapMonth: false 
calendar: gregorian (current) era: 1 year: 2022 month: 12 day: 14 isLeapMonth: false 
calendar: gregorian (current) timeZone: America/New_York (fixed (equal to current)) era: 1 year: 2022 month: 12 day: 17 hour: 9 minute: 24 second: 11 nanosecond: 339460015 weekday: 7 weekdayOrdinal: 3 quarter: 0 weekOfMonth: 3 weekOfYear: 51 yearForWeekOfYear: 2022 isLeapMonth: false 
calendar: gregorian (current) timeZone: America/New_York (fixed (equal to current)) era: 1 year: 2022 month: 12 day: 17 hour: 9 minute: 26 second: 39 nanosecond: 866878032 weekday: 7 weekdayOrdinal: 3 quarter: 0 weekOfMonth: 3 weekOfYear: 51 yearForWeekOfYear: 2022 isLeapMonth: false 
Run Code Online (Sandbox Code Playgroud)

现在selectedDates包含两个“今天日期”...相隔 2 分钟 28 秒。

当我点击日历上的 17 号时,其中没有set要删除的匹配日期...因此当日历刷新时(例如当我选择另一个日期时),17 号仍然显示为选定的。

因此,让我们更改以编程方式插入的内容DateComponents以匹配日历的数据:

let todayDatecomponents = calendar.dateComponents([.calendar, .era, .year, .month, .day], from: Date.now)
selectedDates.insert(todayDatecomponents)
Run Code Online (Sandbox Code Playgroud)

现在,当我们点击17日历时,它将被取消选择,并且匹配的对象selectedDates将被删除。

以下是我修改您的代码以进行调试的方法:

import SwiftUI

@available(iOS 16.0, *)
struct MultiDateView: View {
    @Environment(\.calendar) private var calendar
    
    @State private var selectedDates: Set<DateComponents> = []
    
    @State private var onChangeCounter = 0
    
    var body: some View {
        VStack {
            MultiDatePicker("Select dates", selection: $selectedDates)
                .frame(height: UIScreen.main.bounds.width)
                .onChange(of: selectedDates) { _ in
                    print("onChange")
                    selectedDates.forEach { d in
                        print(d)
                    }
                    print()
                    self.onChangeCounter += 1
                }
            
            Button("Select today") {
                let todayDatecomponents = calendar.dateComponents(in: calendar.timeZone, from: Date.now)
                selectedDates.insert(todayDatecomponents)
            }
            .foregroundColor(.white)
            .frame(minWidth: 150)
            .padding()
            .background(Color.accentColor)
            .cornerRadius(20)
            
            Button("Select today the right way") {
                let todayDatecomponents = calendar.dateComponents([.calendar, .era, .year, .month, .day], from: Date.now)
                selectedDates.insert(todayDatecomponents)
            }
            .foregroundColor(.white)
            .frame(minWidth: 150)
            .padding()
            .background(Color.green)
            .cornerRadius(20)
            
            HStack {
                Text("onChangeCounter")
                Spacer()
                Text(String(onChangeCounter))
            }
            .padding()
            
            Button("Print Date.now in debug console") {
                print("debug")
                print("debug:", Date.now)
                print()
            }
            .foregroundColor(.white)
            .frame(minWidth: 150)
            .padding()
            .background(Color.red)
            .cornerRadius(20)
            
            Spacer()
        }
    }
}

@available(iOS 16.0, *)
struct MultiDateView_Previews: PreviewProvider {
    static var previews: some View {
        MultiDateView()
    }
}
Run Code Online (Sandbox Code Playgroud)