Я работаю над проектом SwiftUI, где мне нужно реализовать функцию с помощью MultiDatePicker, которая позволяет пользователю выбирать целую неделю, нажав одну дату в течение этой недели. После выбора даты вся неделя, к которой она относится, должна быть выбрана автоматически. Если пользователь выбирает дату на другой неделе, выбор ранее выбранной недели следует отменить и вместо нее следует выбрать новую неделю.
import SwiftUI
struct ContentView: View {
@State private var dates: Set<DateComponents> = []
var body: some View {
MultiDatePicker("", selection: $dates)
.frame(height: 300)
.onChange(of: dates) { oldValue, newValue in
if let selectedDate = newValue.first, let date = Calendar.current.date(from: selectedDate) {
selectWeek(for: date)
}
}
}
private func selectWeek(for date: Date) {
let calendar = Calendar.current
let weekBoundary = calendar.weekBoundary(for: date) // this function calculates the whole week
for dayOffset in 0..<7 {
if let dateOfDay = calendar.date(byAdding: .day, value: dayOffset, to: weekBoundary.startOfWeek) {
let components = calendar.dateComponents([.year, .month, .day], from: dateOfDay)
dates.insert(components)
}
}
}
}
Это желаемый результат: введите сюда описание изображения
Проблема в том, что выбор просто срабатывает 1 раз (1-й раз).
Кто-нибудь успешно реализовал аналогичную функцию выбора недели в SwiftUI? Если да, то как вы к этому подошли? Существуют ли альтернативные компоненты или пользовательские реализации, которые могли бы облегчить выбор целой недели более эффективно, чем использование MultiDatePicker?
Вам следует изменить [.year, .month, .day] на [.calendar, .era, .year, .month, .day], чтобы предотвратить ошибки выбора, потому что это формат DateComponents, который ожидает MultiDatePicker. Другие проблемы Sweeper уже упоминались.





Когда вы выбираете другую неделю в средстве выбора, выбранные ранее даты не будут автоматически удалены, поэтому просто использовать dates.first для поиска вновь выбранной даты неверно.
Вам нужно будет найти разницу между новым значением и старым значением dates — тогда вы узнаете, какая новая дата выбрана. Если выбор даты отменен, предположительно вы хотите отменить выбор всей недели, я думаю, было бы лучше поместить эту логику в собственный Binding:
var datesBinding: Binding<Set<DateComponents>> {
Binding {
return dates
} set: { newValue in
let added = newValue.subtracting(dates) // find the difference
// if a new date selected, "added" should contain a single element
if let firstAdded = added.first, let date = calendar.date(from: firstAdded) {
selectWeek(for: date)
} else { // this means a date is deselected, so deselect the whole week
dates = []
}
}
}
@Environment(\.calendar) var calendar
var body: some View {
MultiDatePicker("", selection: datesBinding)
.frame(height: 300)
}
Обратите внимание, что в selectWeek сначала следует установить dates = [], иначе вы не сможете отменить выбор ранее выбранной недели.
@RonniePickering Вы прочитали весь ответ до конца? «Обратите внимание, что в selectWeek сначала следует установить dates = []»
Неважно. Только что увидел ваше последнее предложение. Действительно, я установил для него пустой набор в методе selectWeek, и он работает. Огромное спасибо!
Откуда взялся актер с именем Calendar.weekBoundary(for:) и какова его работа?