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)
}
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.