我正在尝试创建一个 SwiftUI 组件,让用户选择星期几。忽略用户界面,我面临的主要问题是,我试图避免使用自定义枚举,例如:
enum Day { case .m, .tu, ... }
并使用 Swift 的 Calendar API 或任何其他本机 API 来代替。但我不确定如何创建具有多个日期的绑定。
想象一下以下情况: 我有一个 UI 组件,其中有 7 个圆圈代表一周中的每一天。用户点击的圆圈将突出显示并添加到所选日期的列表中。我现在希望不再是自定义枚举,而是原生 Swift 类型。我找不到任何适合我的用例的类型。有什么想法吗?
/// A horizontal container that picks days
struct DaysPicker: View {
// a state property needs to be here to add the days to, of type [calendar], [date] or something similar
var listHasDay: Bool {
true // logic to check if list has the day
}
var body: some View {
HStack(spacing: 12) {
ForEach(1..<8) { day in
Circle()
.fill(
listHasDay ? .red : .blue
)
.frame(
width: 10,
height: 10
)
.overlay {
Text("\(day)")
}
.onTapGesture { // add day to list }
}
}
}
}
}
这是当前组件。另请注意,日子会重复,这是为了闹钟。
一周中的日子是任何日期之后的 7 天,例如今天。因此,如果您构建一个包含今天起接下来 7 个日期的列表,您将获得一周内的所有日期名称,顺序由今天是哪一天决定。
要按特定顺序列出列表(周一或周日为第一个),只需找到从今天开始的下一个周一或周日,然后获取该日期之后的下一个 7 个日期。
您可以使用日期格式化程序来设置您喜欢的日期名称格式。
对于“挑选”日期,您需要一个保存日期名称和关联的布尔值的状态:
@State private var daySelections: [(day: String, isSelected: Bool)] = []
您还应该有一个表示特定日期及其状态的视图,该视图将接受该日期和与 bool 值的绑定作为参数。这将允许您根据需要设置代表日期的视图的样式,并从该子视图中切换选择。
这是工作代码:
import SwiftUI
// A horizontal container that picks days
struct DaysPicker: View {
//A state to hold the days and their selection status
@State private var daySelections: [(day: String, isSelected: Bool)] = []
// Computed property to get selected days
private var selectedDays: [String] {
daySelections.filter { $0.isSelected }.map { $0.day }
}
//Body
var body: some View {
VStack {
HStack(spacing: 0) {
ForEach(daySelections.indices, id: \.self) { index in
let day = daySelections[index].day
DaysCircle(day: day, isSelected: $daySelections[index].isSelected) //pass a binding to the day's isSelected value
}
}
.padding()
VStack {
Text("Selected days: \(selectedDays.count)")
.foregroundStyle(.secondary)
.padding()
ForEach(selectedDays, id: \.self) { day in
Text(day)
.textCase(.uppercase)
.foregroundStyle(.green)
}
}
Spacer()
}
.onAppear {
daySelections = getDayNames()
}
}
//Function to build list of day names with associated bool value for selection
private func getDayNames() -> [(day: String, isSelected: Bool)] {
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale.current
dateFormatter.dateFormat = "E" // "E" gives a short version like "Mon", "Tue"
let calendar = Calendar.current
let today = calendar.startOfDay(for: Date())
let weekday = calendar.component(.weekday, from: today)
let daysUntilMonday = (9 - weekday) % 7
let startOfWeek = calendar.date(byAdding: .day, value: daysUntilMonday, to: today)!
var daysOfWeek: [(String, Bool)] = []
for i in 0..<7 {
if let day = calendar.date(byAdding: .day, value: i, to: startOfWeek) {
let dayName = dateFormatter.string(from: day)
daysOfWeek.append((dayName, false)) // Initialize each day with isSelected as false
}
}
return daysOfWeek
}
}
struct DaysCircle: View {
//Parameters
var day: String
@Binding var isSelected: Bool
//Body
var body: some View {
Button {
withAnimation {
isSelected.toggle()
}
} label: {
Text("\(day)")
.textCase(.uppercase)
.font(.caption)
.fixedSize()
.foregroundStyle(.white)
.padding()
.background(isSelected ? .green : .cyan, in: Circle())
}
}
}
#Preview {
DaysPicker()
}