Home > OS >  SwiftUI - Days of the Week Click
SwiftUI - Days of the Week Click

Time:08-29

I have created a simple "days of the week" reminder section. In that, if "All" is selected all the seven days have to be selected. If I click again, all days have to be unselected. But if I click on the other days only that particular item has to be selected/unselected. Toggle is an Image and DaysOfTheWeek is a Text. Any other optimum approaches are appreciated.

struct Task: Identifiable {
        let id = UUID()
        let name: String
        var isCompleted: Bool
    }
    
    
    @State private var tasks = [
        Task(name: NSLocalizedString("all", comment: "all"), isCompleted: false),
        Task(name: NSLocalizedString("sunday", comment: "sunday day of week"), isCompleted: false),
        Task(name:NSLocalizedString("monday", comment: "monday day of week") , isCompleted: false),
        Task(name:NSLocalizedString("tuesday", comment: "tuesday day of week") , isCompleted: false),
        Task(name:NSLocalizedString("wednesday", comment: "wednesday day of week") , isCompleted: false),
        Task(name:NSLocalizedString("thursday", comment: "thursday day of week"), isCompleted: false),
        Task(name:NSLocalizedString("friday", comment: "friday day of week") , isCompleted: false),
        Task(name:NSLocalizedString("saturday", comment: "saturday day of week") , isCompleted: false),
        
    ]


LazyVGrid(columns: layout, alignment: .leading, spacing: 20){
                                ForEach($tasks) { $task in
                                    ScrollView(.vertical, showsIndicators: false) {
                                        HStack{
                                            Image(systemName: task.isCompleted ? "checkmark.square.fill" : "square")
                                                .foregroundColor(checked ? Color(UIColor.systemBlue) : Color.secondary)
                                                .onTapGesture {
                                                    if task.name == "all" {
                                                        //select all items
                                                        if allSelected {
                                                            
                                                        }
                                                    } else {
                                                        //select only that item
                                                        
                                                    }
                                                }
                                            
                                            Text(task.name)
                                        }
                                        
                                    }
                                }.padding(.horizontal)
                            }

App UI

CodePudding user response:

There are likely more efficient ways to do this, but here's a starting point (see inline comments):

struct TaskModel: Identifiable {
    let id = UUID()
    let name: String
    var isCompleted: Bool
    var isAllItem: Bool = false
}

struct ContentView: View {
    
    @State private var tasks = [
        TaskModel(name: NSLocalizedString("all", comment: "all"), isCompleted: false, isAllItem: true),
        TaskModel(name: NSLocalizedString("sunday", comment: "sunday day of week"), isCompleted: false),
        TaskModel(name:NSLocalizedString("monday", comment: "monday day of week") , isCompleted: false),
        TaskModel(name:NSLocalizedString("tuesday", comment: "tuesday day of week") , isCompleted: false),
        TaskModel(name:NSLocalizedString("wednesday", comment: "wednesday day of week") , isCompleted: false),
        TaskModel(name:NSLocalizedString("thursday", comment: "thursday day of week"), isCompleted: false),
        TaskModel(name:NSLocalizedString("friday", comment: "friday day of week") , isCompleted: false),
        TaskModel(name:NSLocalizedString("saturday", comment: "saturday day of week") , isCompleted: false),
        
    ]
    
    let layout = [GridItem(.flexible()), GridItem(.flexible())]
    
    
    var body : some View {
        LazyVGrid(columns: layout, alignment: .leading, spacing: 20){
            ForEach($tasks) { $task in
                ScrollView(.vertical, showsIndicators: false) {
                    HStack{
                        Image(systemName: task.isCompleted ? "checkmark.square.fill" : "square")
                            .foregroundColor(task.isCompleted ? .blue : Color.secondary)
                            .onTapGesture {
                                let newValue = !task.isCompleted
                                if task.isAllItem {
                                    // Change all of the other items to match the state of
                                    // the "all" item
                                    tasks = tasks.map {
                                        var copy = $0
                                        copy.isCompleted = newValue
                                        return copy
                                    }
                                } else {
                                    task.isCompleted = newValue
                                    // If an item has been turned off,
                                    // make sure that the "all" item is also off
                                    if !newValue {
                                        tasks = tasks.map {
                                            guard $0.isAllItem else { return $0 }
                                            var copy = $0
                                            copy.isCompleted = false
                                            return copy
                                        }
                                    }
                                }
                            }
                        
                        Text(task.name)
                    }
                    
                }
            }.padding(.horizontal)
        }
        .frame(width: 600, height: 600)
    }
    
}

Another option (which I might prefer a bit) is to write a custom Binding that works basically as a reducer:

struct ContentView: View {
    
    @State private var tasks = [
        TaskModel(name: NSLocalizedString("all", comment: "all"), isCompleted: false, isAllItem: true),
        TaskModel(name: NSLocalizedString("sunday", comment: "sunday day of week"), isCompleted: false),
        TaskModel(name:NSLocalizedString("monday", comment: "monday day of week") , isCompleted: false),
        TaskModel(name:NSLocalizedString("tuesday", comment: "tuesday day of week") , isCompleted: false),
        TaskModel(name:NSLocalizedString("wednesday", comment: "wednesday day of week") , isCompleted: false),
        TaskModel(name:NSLocalizedString("thursday", comment: "thursday day of week"), isCompleted: false),
        TaskModel(name:NSLocalizedString("friday", comment: "friday day of week") , isCompleted: false),
        TaskModel(name:NSLocalizedString("saturday", comment: "saturday day of week") , isCompleted: false),
        
    ]
    
    let layout = [GridItem(.flexible()), GridItem(.flexible())]
    
    func bindingForItem(_ task: TaskModel) -> Binding<TaskModel> {
        .init {
            return task
        } set: { task in
            tasks = tasks.map {
                var copy = $0.id == task.id ? task : $0
                if task.isAllItem {
                    copy.isCompleted = task.isCompleted
                } else {
                    if !$0.isCompleted, task.isAllItem {
                        copy.isCompleted = false
                    }
                }
                return copy
            }
        }
    }
    
    var body : some View {
        LazyVGrid(columns: layout, alignment: .leading, spacing: 20){
            ForEach(tasks) { task in
                ScrollView(.vertical, showsIndicators: false) {
                    HStack{
                        Image(systemName: task.isCompleted ? "checkmark.square.fill" : "square")
                            .foregroundColor(task.isCompleted ? .blue : Color.secondary)
                            .onTapGesture {
                                bindingForItem(task).wrappedValue.isCompleted.toggle()
                            }
                        
                        Text(task.name)
                    }
                    
                }
            }.padding(.horizontal)
        }
        .frame(width: 600, height: 600)
    }
    
}

Note that I've changed Task to TaskModel since Swift already has a Task and yours creates a namespace collision.

  • Related