Как сгруппировать основные элементы данных по дате в SwiftUI? - PullRequest
0 голосов
/ 24 марта 2020

В моем приложении iOS есть:

TO DO ITEMS

To do item 3
24/03/2020
------------
To do item 2
24/03/2020
------------
To do item 1
23/03/2020
------------

Я хотел бы иметь:

TO DO ITEMS


24/03

To do item 3
24/03/2020
------------
To do item 2
24/03/2020
------------


23/03

To do item 1
23/03/2020
------------

==== ===========

Что у меня пока есть:

Я использую Базовые данные и имею 1 Сущность: Todo . Модуль: Модуль текущего продукта. Codegen: определение класса. Этот объект имеет 2 атрибута: заголовок (строка), дата (дата) .

ContentView.swift Отображение списка.

import SwiftUI

struct ContentView: View {
    @Environment(\.managedObjectContext) var moc
    @State private var date = Date()
    @FetchRequest(
        entity: Todo.entity(),
        sortDescriptors: [
            NSSortDescriptor(keyPath: \Todo.date, ascending: true)
        ]
    ) var todos: FetchedResults<Todo>

    @State private var show_modal: Bool = false

    var dateFormatter: DateFormatter {
        let formatter = DateFormatter()
        formatter.dateStyle = .short
        return formatter
    }

    // func to group items per date. Seemed to work at first, but crashes the app if I try to add new items using .sheet
    func update(_ result : FetchedResults<Todo>)-> [[Todo]]{
        return  Dictionary(grouping: result){ (element : Todo)  in
            dateFormatter.string(from: element.date!)
        }.values.map{$0}
    }

    var body: some View {
        NavigationView {
            VStack {
                List {
                    ForEach(update(todos), id: \.self) { (section: [Todo]) in
                        Section(header: Text( self.dateFormatter.string(from: section[0].date!))) {
                            ForEach(section, id: \.self) { todo in
                                HStack {
                                    Text(todo.title ?? "")
                                    Text("\(todo.date ?? Date(), formatter: self.dateFormatter)")
                                }
                            }
                        }
                    }.id(todos.count)

                    // With this loop there is no crash, but it doesn't group items
                    //ForEach(Array(todos.enumerated()), id: \.element) {(i, todo) in
                    //    HStack {
                    //        Text(todo.title ?? "")
                    //        Text("\(todo.date ?? Date(), formatter: self.dateFormatter)")
                    //    }
                    //}

                }
            }
            .navigationBarTitle(Text("To do items"))
            .navigationBarItems(
                trailing:
                Button(action: {
                    self.show_modal = true
                }) {
                    Text("Add")
                }.sheet(isPresented: self.$show_modal) {
                    TodoAddView().environment(\.managedObjectContext, self.moc)
                }
            )
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
        return ContentView().environment(\.managedObjectContext, context)
    }
}

TodoAddView.swift В этом представлении я добавляю новый элемент.

import SwiftUI

struct TodoAddView: View {

    @Environment(\.presentationMode) var presentationMode
    @Environment(\.managedObjectContext) var moc

    static let dateFormat: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateStyle = .medium
        return formatter
    }()

    @State private var showDatePicker = false
    @State private var title = ""
    @State private var date : Date = Date()

    var body: some View {
        NavigationView {

            VStack {
                HStack {
                    Button(action: {
                        self.showDatePicker.toggle()
                    }) {
                        Text("\(date, formatter: Self.dateFormat)")
                    }

                    Spacer()
                }

                if self.showDatePicker {
                    DatePicker(
                        selection: $date,
                        displayedComponents: .date,
                        label: { Text("Date") }
                    )
                        .labelsHidden()
                }

                TextField("title", text: $title)

                Spacer()

            }
            .padding()
            .navigationBarTitle(Text("Add to do item"))
            .navigationBarItems(
                leading:
                Button(action: {
                    self.presentationMode.wrappedValue.dismiss()
                }) {
                    Text("Cancel")
                },

                trailing:
                Button(action: {

                    let todo = Todo(context: self.moc)
                    todo.date = self.date
                    todo.title = self.title

                    do {
                        try self.moc.save()
                    }catch{
                        print(error)
                    }

                    self.presentationMode.wrappedValue.dismiss()
                }) {
                    Text("Done")
                }
            )
        }
    }
}

struct TodoAddView_Previews: PreviewProvider {
    static var previews: some View {
        TodoAddView()
    }
}

Я пробовал это: Я искал несколько примеров. Один из них выглядел хорошо: Как правильно сгруппировать список, извлеченный из CoreData по дате? , и я использовал функцию обновления и ForEach оттуда, но она не работает с .sheet в SwiftUI. Когда я открываю .sheet (после нажатия Add), приложение вылетает с ошибкой:

Thread 1: Исключение: «Попытка создать две анимации для ячейки»

Как это исправить? Или есть другой способ группировки основных данных по дате? Мне сказали, что я должен добавить группирование к моей модели данных. И просто показать это позже в пользовательском интерфейсе. Я не знаю с чего начать.

Еще одно предположение состоит в том, что я, возможно, мог бы отредактировать свой код @FetchRequest, чтобы добавить туда группировку. Но я ищу решение несколько дней без удачи. Я знаю, что в Core Data есть setPropertiesToGroupBy, но я не знаю, как и как он работает с @FetchRequest и SwiftUI.

Еще одно предположение: Можно ли использовать словарь (группировка : attributeName) для группировки экземпляров CoreData Entity в SwiftUI на основе их атрибутов? Группировка массивов выглядит так просто: https://www.hackingwithswift.com/example-code/language/how-to-group-arrays-using-dictionaries, но я не знаю, как и как это работает с Core Data и @ FetchRequest.

1 Ответ

1 голос
/ 28 марта 2020

Я сам вхожу в SwiftUI, так что это может быть недоразумение, но я думаю, что проблема в том, что функция update нестабильна, в том смысле, что она не гарантирует возвращение групп в том же порядке. каждый раз. Следовательно, SwiftUI запутывается, когда добавляются новые элементы. Я обнаружил, что ошибок удалось избежать путем специальной сортировки массива:

func update(_ result : FetchedResults<Todo>)-> [[Todo]]{
    return  Dictionary(grouping: result){ (element : Todo)  in
        dateFormatter.string(from: element.date!)
    }.values.sorted() { $0[0].date! < $1[0].date! }
}
...