Как обновить поля, которые зависят от текущего времени после выборки основных данных, но до отображения табличного представления? - PullRequest
0 голосов
/ 24 марта 2019

РЕЗЮМЕ ПРОБЛЕМЫ

Я пишу приложение для iOS, которое похоже на гибрид между списком дел и календарем.В отличие от списка дел, вы не отметите все как завершенное.Скорее, задачи делятся на секции в UITableView для активных и неактивных.Проблема в том, что раздел и сортировка могут измениться в любое время относительно текущей даты и времени.

Так что в настоящее время я больше всего борюсь с тем, как и когда обновлять дату section / nextAlert при начальной загрузке приложения последанные извлекаются, но до того, как контроллер представления начинает их отображать.

ПРЕДПОСЫЛКИ НА ЧТО Я ПОПРОБОВАЛ

Я изучил и попробовал много разных вещей, чтобы обратиться кпроблемы.

  1. Обновить в viewDidLoad после получения .Я попытался вызвать функцию refreshReminders сразу после выборки в контроллере табличного представления во время viewDidLoad.Кажется, это работает нормально, пока я не сохраняю данные ядра в этой функции.Простая установка полей в записи / записях запускает контроллер, и строки перемещаются и обновляются, как они должны.Хотя кажется, что это слишком поздно в последовательности загрузки.Действительно ли мы хотим, чтобы табличное представление перемещало записи прямо при первом запуске приложения?Это звучит как плохая практика.

Теперь, если я попытаюсь сохранить изменения в основных данных после обновления, это то место, где оно становится действительно странным.Таблица явно обновляется на основе изменений в записях в текущем контексте, но когда контекст сохраняется, он запускает изменения снова!Поскольку изменения уже были внесены, приложение падает при попытке переместить строку из старого местоположения в новое после того, как оно уже было перемещено.См. Код ниже.

Обновить до пробуждения Fetch .Я также попытался использовать расширение основного объекта данных (напоминания), чтобы установить раздел и дату следующего оповещения.Тем не менее, это, похоже, ничего не делает.т.е. таблица все еще загружается с напоминаниями в неправильном разделе и / или порядке сортировки.

Обновление полей здесь также не помечает данные как «грязные» (измененные), поэтому контроллер табличного представления не перемещает их, и сохранение контекста, которое запускается, только если данныегрязный не срабатывает, конечно.Я даже пытался сохранить контекст после извлечения, не проверяя, изменился ли он, но это тоже ничего не делает.т.е. данные ядра действительно не думают, что что-то изменилось, даже если оно изменилось.

ЗДЕСЬ КОД

Это случай .move в функции контроллера моего tableViewController

case .move:
            if let oldPath = indexPath, let newPath = newIndexPath {
                os_log("RemindersViewController: Move was triggered, now updating row in table. Old path was %{public}@ and new path is %{public}@", log: OSLog.default, type: .info, oldPath as CVarArg, newPath as CVarArg)
RemindersCell, withReminder: anObject as! Reminders)
                configureCell(tableView.cellForRow(at: oldPath) as! RemindersCell, withReminder: anObject as! Reminders)
                os_log("RemindersViewController: updated moved cell.", log: OSLog.default, type: .info)


                // Don't actually try to move it if the old and new path are the same
                if (newPath != oldPath) {
                    os_log("RemindersViewController: Moving row in table.", log: OSLog.default, type: .info)
                    tableView.moveRow(at: oldPath, to: newPath)
                    os_log("RemindersViewController: row moved.", log: OSLog.default, type: .info)
                }
            }

Это упрощенная версия моей функции configureCell.

    func configureCell(_ cell: RemindersCell, withReminder reminder: Reminders) {
        cell.labelTitleField!.text = reminder.title ?? "New Reminder"
        cell.labelAlertField!.text = reminder.nextAlert!.description
    }

Я использую beginUpdates () и endUpdates () для пакетных обновлений, чтобы при наличии множества измененийОперационная система может сразу определить лучший способ обработки изменений и анимировать все движения одновременно.

// Batch the updates to the table. Start with beginUpdates so all the action animations are queued up.
    func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        tableView.beginUpdates()
    }
// Batch the updates to the table. End with endUpdates to trigger the actual animations.
    func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        tableView.endUpdates()
    }

Это то, что в консоли связано с этим кодом:

2019-03-23 12:31:09.307801-0500 Scheduler[5711:2218287] RemindersViewContoller in viewDidLoad: Fetched records successfully.
Refreshing reminders!
2019-03-23 12:31:09.311755-0500 Scheduler[5711:2218287] Reminder 'Test Non-recurring Reminder' section updated to Inactive.
2019-03-23 12:31:09.313254-0500 Scheduler[5711:2218287] RemindersViewController: Move was triggered, now updating row in table. Old path was <NSIndexPath: 0x28078e480> {length = 2, path = 0 - 0} and new path is <NSIndexPath: 0x28078f140> {length = 2, path = 1 - 2}
Scheduler was compiled with optimization - stepping may behave oddly; variables may not be available.
(lldb) 

Таким образом, похоже, что сама ОС переместила напоминание, где она находится, каким-то образом без использования функции моего контроллера.Это должно произойти, когда отдельные поля напоминания обновляются, но затем, когда я сохраняю контекст в основных данных, он снова вызывает функцию моего контроллера, чтобы выполнить уже выполненное перемещение.

ОЖИДАЕМЫЙ И АКТУАЛЬНЫЙРЕЗУЛЬТАТЫ

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

ОБНОВЛЕНИЯ

Вот процедура refreshReminder, которая прекрасно работает до тех пор, пока сохранение в основные данные закомментировано.Эта функция вызывается сразу после того, как я выполню начальную выборку записей напоминания:

// Handle updating nextAlert and section based on current date and time
func refreshReminders () {

        // Loop through the records and update the time-sensitive section and nextAlert fields
        print("Refreshing reminders!")
        for reminder in fetchedController.fetchedObjects! {
            if (reminder.nextAlert! < Date()) {
                if (reminder.recurrence != "Never") {
                    let nextAlert = nextAlertDate(alertDate: reminder.alert!, recurrencePattern: reminder.recurrence)
                    if (reminder.nextAlert != nextAlert) {
                        reminder.nextAlert = nextAlert
                        os_log("Reminder '%{public}@' nextAlert date updated to %{public}@.", reminder.title!, String(describing: reminder.nextAlert!))
                    }
                }
                let section = getSection(nextAlertDate: reminder.nextAlert!)
                if (reminder.section != section) {
                    reminder.section = section
                    //print("Reminder section updated to", reminder.section!)
                    os_log("Reminder '%{public}@' section updated to %{public}@.", reminder.title!, reminder.section!)
                }
            }
        } //endfor

        // Save changes to core data if there are any
        /*if context.hasChanges {
             do {
             try context.save()
                 print("RemindersViewController in refreshReminders: Changes to core data, so saving them now.")
             } catch {
                 // Replace this implementation with code to handle the error appropriately.
                 // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                 let nserror = error as NSError
                 fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
             } //enddo
         }*/ //endif

    } //endfunc refreshReminders

Вот результаты обновления / перемещения напоминания из одного раздела в другой при первом запуске приложения, поскольку прошло время напоминания:

2019-03-23 18:26:24.796406-0500 Scheduler[6383:2371662] AppDelegate: Initialized core data stack
2019-03-23 18:26:24.801211-0500 Scheduler[6383:2371734] AppDelegate: Notification authorization granted.
2019-03-23 18:26:24.806165-0500 Scheduler[6383:2371734] AppDelegate: Set our custom notification categories and actions.
2019-03-23 18:26:24.810726-0500 Scheduler[6383:2371662] RemindersViewContoller in viewDidLoad: Fetched records successfully.
Refreshing reminders!
2019-03-23 18:26:24.814331-0500 Scheduler[6383:2371662] Reminder 'Test Non-recurring Reminder' section updated to Inactive.
Number of sections: 2
2019-03-23 18:26:24.815463-0500 Scheduler[6383:2371662] RemindersViewController in viewWillAppear: We're here. Let's see how often we get triggered!
Number of sections: 2
Number of records in section 1 : 3
Number of records in section 0 : 7
Number of sections: 2
Number of records in section 1 : 3
Number of records in section 0 : 7
2019-03-23 18:26:24.893329-0500 Scheduler[6383:2371662] RemindersViewController: Move was triggered, now updating row in table. Old path was <NSIndexPath: 0x28047a900> {length = 2, path = 0 - 0} and new path is <NSIndexPath: 0x28047a920> {length = 2, path = 1 - 3}
2019-03-23 18:26:24.893498-0500 Scheduler[6383:2371662] RemindersViewController: updated moved cell.
2019-03-23 18:26:24.893511-0500 Scheduler[6383:2371662] RemindersViewController: Moving row in table.
2019-03-23 18:26:24.893524-0500 Scheduler[6383:2371662] RemindersViewController: row moved.
Number of sections: 2
Number of sections: 2
Number of records in section 0 : 6
Number of records in section 1 : 4

Вот os_log и ошибка, когда я раскомментирую сохранение в данные ядра. Вылетает в configCell.

2019-03-23 18:35:22.210467-0500 Scheduler[6396:2375020] AppDelegate: Initialized core data stack
2019-03-23 18:35:22.215964-0500 Scheduler[6396:2375092] AppDelegate: Notification authorization granted.
2019-03-23 18:35:22.220937-0500 Scheduler[6396:2375092] AppDelegate: Set our custom notification categories and actions.
2019-03-23 18:35:22.227273-0500 Scheduler[6396:2375020] RemindersViewContoller in viewDidLoad: Fetched records successfully.
Refreshing reminders!
2019-03-23 18:35:22.230832-0500 Scheduler[6396:2375020] Reminder 'Test Non-recurring Reminder' section updated to Inactive.
2019-03-23 18:35:22.232326-0500 Scheduler[6396:2375020] RemindersViewController: Move was triggered, now updating row in table. Old path was <NSIndexPath: 0x282e6d360> {length = 2, path = 0 - 0} and new path is <NSIndexPath: 0x282e6df60> {length = 2, path = 1 - 3}
Scheduler was compiled with optimization - stepping may behave oddly; variables may not be available.
(lldb) 
configureCell(tableView.cellForRow(at: oldPath) as! RemindersCell, withReminder: anObject as! Reminders)
Thread 1: EXC_BREAKPOINT (code=1, subcode=0x104048220)

1 Ответ

0 голосов
/ 01 апреля 2019

Как оказалось, возможно, что запрос на перемещение строки может быть отправлен, даже если сама ячейка равна нулю.PaulW11 указывает на то, что, скорее всего, ячейка еще не видна на экране.Таким образом, способ справиться с этим - обновить (настроить) ячейку тогда и только тогда, когда она не равна нулю, а продолжить и обработать перемещение строки, пока у нас есть действительные oldPath и newPath.Мой обновленный код, который отлично работает:

case .move:
            // If we have an old and new path, proceed with moving and/or updating the cell
            if let oldPath = indexPath, let newPath = newIndexPath {

                if let cell = tableView.cellForRow(at: oldPath) as? RemindersCell, let reminder = anObject as? Reminders {
                    os_log("RemindersViewController: Move was triggered, so updating cell in table. Reminder is: %{public}@", log: .default, type: .info, reminder)
                    configureCell(cell, withReminder: reminder)
                    os_log("RemindersViewController: Updated moving cell.", log: .default, type: .info)
                } else {
                    os_log("RemindersViewController: Move triggered, but cell isn't yet visible so skipping updating the cell fields.")
                }

            // If we have an old and new path, then go ahead and move the cell
            os_log("RemindersViewController: Moving row in table.", log: .default, type: .info)
            tableView.moveRow(at: oldPath, to: newPath)
                os_log("RemindersViewController: Row moved from %{public}@ to %{public}@.", log: .default, type: .info, oldPath as CVarArg, newPath as CVarArg)

            // If old and new path invalid, then log that
            } else {
                os_log("RemindersViewController: Move triggered, but old and new path aren't filled, so ignoring it.", log: .default, type: .error)
            }
            break
...