Swift split view controller, проблема с «повторным» уведомлением при первой загрузке - PullRequest
0 голосов
/ 17 сентября 2018

Полное название: Swift 4.2 с использованием контроллера разделенного вида, ошибка с «повторным» уведомлением при первой загрузке, которое нарушает возможность программного выбора «нового» объекта в представлении главной таблицы.

Фон

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

Я реализовал Базовые Данные.

В основной таблице отображается список объектов.Источником данных для представления главной таблицы является контроллер выборочных результатов.

Подробное табличное представление показывает атрибуты и связанные значения отношений текущей выбранной строки (сущности) в главном табличном представлении.Источником данных для подробного табличного представления является объект, относящийся к текущей выбранной строке в главном табличном представлении, которая передается с помощью сеанса «Показать подробности».

На устройствах с большими экранами, где splitViewController.isCollapsed == false, оба вида основного и подробного таблиц активны на экране, как показано на рисунке ниже.

Master and Detail table views on screen using iPad Pro 2nd gen simulator

Достаточно стандартное расположение для приложения, управляемого данными ...?

Поток логики

Когда пользователь обновляет существующую сущность (в случае скриншота)Например, существующее «Событие»), они нажимают кнопку Save на панели навигации представления таблицы сведений.

Поскольку сущность уже существует, в методе делегата контроллера извлеченных результатов контроллера controller(_:didChange:at:for:newIndexPath) в 1025 *

  • в case .update внедрить tableView.reloadRows(at: [indexPath!], with: UITableViewRowAnimation.none), которыйтриггеры tableView(_willDisplay:forRowAt:), которые в свою очередь обновляют форматирование выбранной ячейки табличного представления;И

  • в case .insert, case .update и case .move, я установил временное значение для текущего IndexPath indexPathForManagedObjectChanged

Метод делегата контроллера результатов выборки:

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {

    switch type {
        case .insert:
            tableView.insertRows(at: [newIndexPath!], with: .fade)
            indexPathForManagedObjectChanged = newIndexPath
            print("___controllerDidChangeObject: INSERTED OBJECT")
        case .delete:
            tableView.deleteRows(at: [indexPath!], with: .fade)
            print("___controllerDidChangeObject: DELETED OBJECT")
        case .update:
            configureCell(tableView.cellForRow(at: indexPath!)!, withEvent: anObject as! PT_Events)
            indexPathForManagedObjectChanged = indexPath
            tableView.reloadRows(at: [indexPath!], with: UITableView.RowAnimation.none)
            print("___controllerDidChangeObject: UPDATED OBJECT")
        case .move:
            configureCell(tableView.cellForRow(at: indexPath!)!, withEvent: anObject as! PT_Events)
            indexPathForManagedObjectChanged = newIndexPath
            tableView.moveRow(at: indexPath!, to: newIndexPath!)
            print("___controllerDidChangeObject: MOVED OBJECT")
    }
}

Затем в методе делегата контроллера таблицы просмотра 1059 * controllerDidChangeContent(_:) я получаю следующий код ...

guard let indexPathOfRowToSelect: IndexPath = indexPathForManagedObjectChanged else {
    return
}
indexPathForManagedObjectChanged = nil
tableView.selectRow(at: indexPathOfRowToSelect, animated: false, scrollPosition: UITableViewScrollPosition.none)

Это гарантирует, что после вставки, обновления и перемещения будет выбрана строка, соответствующая данным в подробном табличном представлении.

Я использую Уведомление типа UserDefaults.didChangeNotification.

Наблюдательдобавлен в Master Table View Controller ( EDIT был viewDidLoad, но теперь) viewWillAppear(_:) метод.Наблюдатель удаляется в методе viewWillDisappear(_:) контроллера главной таблицы.

Если пользователь изменяет одну из настроек, уведомление observer вызывает следующую функцию ...

@objc
func userDefaultSettingsDidChange(_ notification: Notification) {

    if (notification.object as? UserDefaults) != nil {            
        NSFetchedResultsController<NSFetchRequestResult>.deleteCache(withName: classCacheName)
        fetchedResultsController = nil
        tableView.reloadData() 
    }
}

Проблема

При первом запуске Master Table View Controller есть Уведомление, которое вызывает метод userDefaultSettingsDidChange, но вызывает это ПОСЛЕ первого запуска метода Fetched Results Controller Delegate.в ответ на первое взаимодействие с пользователем.Это несмотря на отсутствие изменений в пользовательских настройках по умолчанию.

Проблема возникает на симуляторе и на устройстве.

Я добавил стек print() с (удален для простоты чтения кода), чтобы отслеживать, что происходит в каком порядке.

Консоль регистрирует функцию userDefaultSettingsDidChange как выполняющуюся ПОСЛЕ того, как завершены методы делегирования контроллера результатов - НО только при первом запуске.

Из-за этого вызов reloadData стирает мой предыдущий вызов selectRow.

Решение

Я могу завершить свой вызовк методу экземпляра selectRow(at:animated:scrollPosition:) в методе controllerDidChangeContent(_:) Извлеченные результаты контроллера делегата основного представления контроллера таблицы для включения небольшой задержки ...

func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
    tableView.endUpdates()

    guard let indexPathOfRowToSelect: IndexPath = indexPathForManagedObjectChanged else {
        return
    }
    indexPathForManagedObjectChanged = nil
    let when = DispatchTime.now() + 0.20
    DispatchQueue.main.asyncAfter(deadline: when) {       
        self.tableView.selectRow(at: indexPathOfRowToSelect, animated: false, scrollPosition: UITableView.ScrollPosition.none)
    }
}

Это работает!

Но - яЯ не удовлетворен.

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

Может кто-нибудь объяснить, почему я вижу это отложенное «обновление» пользовательских настроек, которое наносит ущерб только при первом взаимодействии пользователя в моем пользовательском интерфейсе?

...