我有一个SwiftUI View
,它包含一个DatePicker
和.datePickerStyle(.wheel)
。我希望用户能够选择过去到当前时间的日期和时间,但不能选择未来的日期或时间。
有一个非常简单的方法可以实现大部分目标:
struct ContentView: View {
@State var selectedDate = Date()
var body: some View {
VStack {
DatePicker("Select Date and Time",
selection: $selectedDate,
in: ...Date(),
displayedComponents: [.date, .hourAndMinute])
.labelsHidden()
.datePickerStyle(.wheel)
}
.padding()
}
}
但是,在下一分钟开始后,最大值将过期:用户只能根据创建View
的日期和时间选择时间。
例如,如果包含DatePicker
的View
在12:44呈现,则一旦时间是12:45,用户仍然只能拾取直到并包括12:44的时间,而不能拾取12:45。
为了解决这个问题,我创建了一个ViewModel,其中@Published
maxDate
每分钟更新一次,并将其用作DatePicker
:
class ContentViewModel: ObservableObject {
@Published var maxDate = Date()
private var timer: Timer?
init() {
updateMaxDate()
}
func updateMaxDate() {
timer?.invalidate()
let now = Date()
let calendar = Calendar.current
let currentMinuteStart = calendar
.date(bySettingHour: now.hour,
minute: now.minute,
second: 0,
of: now)!
let nextMinuteStart = calendar
.date(byAdding: .minute,
value: 1,
to: currentMinuteStart)!
let remainingSeconds = calendar
.dateComponents([.second],
from: now,
to: nextMinuteStart)
if let seconds = remainingSeconds.second {
maxDate = currentMinuteStart
timer = Timer
.scheduledTimer(withTimeInterval: TimeInterval(seconds),
repeats: false)
{ [weak self] _ in
self?.timerFired()
}
}
}
private func timerFired() {
print("timerFired()")
updateMaxDate()
}
deinit {
timer?.invalidate()
}
}
struct ContentView: View {
@StateObject private var viewModel = ContentViewModel()
@State var selectedDate = Date()
var body: some View {
VStack {
DatePicker("Select Date and Time",
selection: $selectedDate,
in: ...viewModel.maxDate,
displayedComponents: [.date, .hourAndMinute])
.labelsHidden()
.datePickerStyle(.wheel)
}
.padding()
}
}
extension Date {
var hour: Int { Calendar.current.component(.hour, from: self) }
var minute: Int { Calendar.current.component(.minute, from: self) }
}
这是可行的,但有一个问题,我不能弄清楚:**我希望timerFired
函数在每一分钟都只被调用一次,但实际上它被调用了数百次。timerFired
被调用的确切次数各不相同,但似乎通常在700-1000次左右,并导致明显的CPU峰值。
我以为这可能与视图刷新或ContentViewModel
对象的多个示例有关,但它是一个@StateObject
,所以它只应该被创建一次,并且我能够通过向其init
添加print
语句来确认它只被初始化一次。
如何解决此问题,使maxDate
只更新一次?或者,是否有更好的方法来设置DatePicker
以实现相同的行为?
1条答案
按热度按时间oaxa6hgo1#
我通过更新ContentViewModel找到了一个解决方案,如下所示:
在进一步的测试中,当我使用
DateComponent
方法来确定到下一分钟的秒数时,它会在新的一分钟之前的几分之一秒内调度一个计时器,并且由于每个计时器触发都会调度另一个计时器,它会触发很多次,直到新的一分钟真正开始。我切换到从60减去,我知道这不是
DateComponent
的方式,但它绕过了这个分数秒的问题,我不能预见会有任何重大问题。更新:我找到了一个使用
DateComponent
的工作解决方案经过进一步的实验,我找到了一个解决方案,将我的工作答案与
DateComponent
方法结合起来,同时避免了函数被调用数百次的问题: