Для вдохновения, как использовать вашу модель, вот упрощенный пример
import SwiftUI // it imports all the necessary stuff ...
Для нашей задачи нам нужна некоторая структура данных, соответствующая Identifiable (это поможет SwiftUI идентифицировать каждый динамически генерируемый ToDoView)
struct ToDo: Identifiable {
let id = UUID()
let date: Date
let task: String
var done = false
}
Простая модель со всеми основными функциями c может быть определена как
class ToDoModel: ObservableObject {
@Published var todo: [ToDo] = []
func groupByDay()->[Int:[ToDo]] {
let calendar = Calendar.current
let g: [Int:[ToDo]] = todo.reduce([:]) { (res, todo) in
var res = res
let i = calendar.ordinality(of: .day, in: .era, for: todo.date) ?? 0
var arr = res[i] ?? []
arr.append(todo)
arr.sort { (a, b) -> Bool in
a.date < b.date
}
res.updateValue(arr, forKey: i)
return res
}
return g
}
}
Там нет ничего особенного, я заполню ее некоторыми случайно запланированными задачами позже, и я определил в модели функция, которая возвращает словарь массива отсортированных задач, где ключ словаря основан на части даты запланированной даты (дата и время). Все задачи будут случайным образом запланированы в интервале от 0 до 50000 секунд с момента «сейчас»
Остаток - это код SwiftUI, который не требует пояснений
struct ContentView: View {
@ObservedObject var model = ToDoModel()
var body: some View {
VStack {
Button(action: {
let todos: [ToDo] = (0 ..< 5).map { (_) in
ToDo(date: Date(timeIntervalSinceNow: Double.random(in: 0 ... 500000)), task: "task \(Int.random(in: 0 ..< 100))")
}
self.model.todo.append(contentsOf: todos)
}) {
Text("Add 5 random task")
}.padding()
Button(action: {
self.model.todo.removeAll { (t) -> Bool in
t.done == true
}
}) {
Text("Remove done")
}.padding()
List {
ForEach(model.groupByDay().keys.sorted(), id: \.self) { (idx) in
Section(header: Text(self.sectionDate(section: idx)), content: {
ForEach(self.model.groupByDay()[idx]!) { todo in
ToDoView(todo: todo).environmentObject(self.model)
}
})
}
}
}
}
// this convert back section index (number of days in current era) to date string
func sectionDate(section: Int)->String {
let calendar = Calendar.current
let j = calendar.ordinality(of: .day, in: .era, for: Date(timeIntervalSinceReferenceDate: 0)) ?? 0
let d = Date(timeIntervalSinceReferenceDate: 0)
let dd = calendar.date(byAdding: .day, value: section - j, to: d) ?? Date(timeIntervalSinceReferenceDate: 0)
let formater = DateFormatter.self
return formater.localizedString(from: dd, dateStyle: .short, timeStyle: .none)
}
}
struct ToDoView: View {
@EnvironmentObject var model: ToDoModel
let todo: ToDo
var body: some View {
VStack {
Text(todoTime(todo: todo)).bold()
Text(todo.task).font(.caption)
Text(todo.done ? "done" : "active").foregroundColor(todo.done ? Color.green: Color.orange).onTapGesture {
let idx = self.model.todo.firstIndex { (t) -> Bool in
t.id == self.todo.id
}
if let idx = idx {
self.model.todo[idx].done.toggle()
}
}
}
}
// returns time string
func todoTime(todo: ToDo)->String {
let formater = DateFormatter.self
return formater.localizedString(from: todo.date, dateStyle: .none, timeStyle: .short)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Если вы хотите использовать переключатель, вы должны быть осторожны, в противном случае удаление задач, назначенных как «выполнено», приведет к sh.
struct ToDoView: View {
@EnvironmentObject var model: ToDoModel
let todo: ToDo
var idx: Int? {
self.model.todo.firstIndex { (t) -> Bool in
t.id == self.todo.id
}
}
var body: some View {
VStack(alignment: .leading) {
Text(todoTime(todo: todo)).bold()
Text(todo.task).font(.caption)
Text(todo.done ? "done" : "active").foregroundColor(todo.done ? Color.green: Color.orange).onTapGesture {
self.model.todo[self.idx!].done.toggle()
}
// using toggle needs special care!!
// we have to "remove" it before animation transition
if idx != nil {
Toggle(isOn: $model.todo[self.idx!].done) {
Text("done")
}
}
}
}
// returns time string
func todoTime(todo: ToDo)->String {
let formater = DateFormatter.self
return formater.localizedString(from: todo.date, dateStyle: .none, timeStyle: .short)
}
}
На 11.3 требуется другой «трюк», см. SwiftUI Toggle в VStack смещен для получения дополнительной информации.