星期选择器

问题描述 投票:0回答:1

我正在尝试创建一个 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 }
                    }
            }
        }
    }
}

这是当前组件。另请注意,日子会重复,这是为了闹钟。

ios swift date swiftui calendar
1个回答
0
投票

一周中的日子是任何日期之后的 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()
}

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.