Может ли UITableViewDiffableDataSource обнаружить изменение элемента?
(Вопрос был переписан после обсуждения с @AndreasOetjen ниже. Спасибо за его комментарии.)
У меня возникла проблема с использованием UITableView
с диффузным источником данных. В моем приложении, когда пользователь изменяет элемент, он может изменить другой элемент, который отображается в той же таблице. Проблема в том, что после того, как я создал и применил новый снимок, содержащий новые значения обоих элементов, пользовательский интерфейс косвенно измененного элемента не обновился.
Сначала я подумал, что диффузный источник данных может обнаружить изменение значения элемента в другом снимке. Например, это может работать следующим образом: если он обнаружил, что оба снимка содержат один и тот же элемент (то есть элементы в обоих снимках имеют одинаковое хеш-значение), он сравнил их значения и обновил строку этого элемента в табличном представлении, если значение изменилось. Однако позже я понял, что, возможно, это не так, потому что источник данных diff не определяет какой-либо API для получения и сравнения значения элемента (моя первоначальная мысль заключалась в том, что он использовалdescription
вычисляемая собственность и ==
операция, но теперь считаю, что это неправда).
Итак, мое текущее понимание заключается в том, что источник данных diffable использует хэш элемента для обнаружения изменения порядка элементов (т. Е. Вставлен новый элемент, старый элемент все еще существует и т. Д.) Вместо изменения значения элемента (т. Е. Старый элемент все еще существует, но его значение изменилось.). Если это понимание верно, тогда возникает следующий вопрос: как использовать доступный источник данных для реализации следующего сценария?
- У предмета есть несколько свойств. Одно свойство (назовем его свойством A) отображается в пользовательском интерфейсе, но не используется для генерации хэша.
- Элемент существует как в старых, так и в новых снимках, но его свойство A изменяется. Поэтому его интерфейс необходимо обновить.
В старом UITableView
API, это можно реализовать, позвонив reloadRows()
или reloadData()
. Но как это сделать, используя доступный источник данных?
ОБНОВЛЕНИЕ:
Потратив время на эксперименты и решение проблемы, я считаю, что понимание в приведенном выше вопросе было неверным. Пожалуйста, смотрите мой ответ ниже. Я считаю, что это объясняет, как работает диффузный источник данных. Я надеюсь, что это поможет другим, у которых будет такое же замешательство. Я был бы рад оказаться неправым. В самом деле. Так что оставьте, пожалуйста, свой ответ, если вы думаете иначе.
3 ответа
После почти однодневных бессмысленных экспериментов, я считаю, что понял, как работает диффузный источник данных, и решил свою проблему на основе этого понимания (оказалось, что моя первоначальная мысль была почти правильной).
Источник дифференцируемых данных использует хэш элемента для идентификации элемента. Для одного и того же элемента, который существует как в старых, так и в новых снимках, источник дифференциальных данных проверяет, изменяется ли элемент, выполняя операцию "==" с его старым и новым значениями.
Разобравшись, это выглядит довольно очевидным и простым подходом. Но это настолько фундаментально, что я не могу понять, почему это нигде не упоминается явно.
Итак, чтобы ответить на мой первоначальный вопрос, да, источник данных diff может обнаруживать изменение значения элемента. Тем не менее, становится сложно, когда значение элемента относится к ссылочному типу и / или текст, показанный в строке, является, скажем, свойствами объектов, на которые ссылается этот объект (например, отношения в Core Data) и т. Д.
Еще одно замечание. Используется ли структура элемента целиком или только ее часть для генерации хэша элемента, не имеет значения, если она идентифицирует элемент. Я предпочитаю использовать только ту часть предмета, которая действительно его идентифицирует.
У меня та же проблема. И после некоторых исследований, я думаюHashable
это не способ обработки функции обновления. Вы можете увидеть это из документа здесь: https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/updating_collection_views_using_diffable_data_sources .
У него есть 2 способа загрузить источник данных с возможностью вывода:Load the Diffable Data Source with Identifiers
иPopulate Snapshots with Lightweight Data Structures
.
В то время как первый рекомендуется Apple. В котором мы используемsnapshot.reconfigureItems
для обновления существующих элементов.
struct Recipe: Identifiable, Codable {
var id: Int
var title: String
// and many other properties
xxxxx
}
// Get the diffable data source's current snapshot.
var snapshot = recipeListDataSource.snapshot()
// Update the recipe's data displayed in the collection view.
snapshot.reconfigureItems([recipeId])
recipeListDataSource.apply(snapshot, animatingDifferences: true).
Дело в том, что вместо использованияRecipe
на снимке мы используемRecipe.ID
, типNSDiffableDataSourceSnapshot<RecipeListSection, Recipe.ID>
.
Что касается второго способа, который мы все используем, помещая модели Hashable в снимок, вот что Apple говорит об этом:
Недостатком этого подхода является то, что источник данных с возможностью вывода больше не может отслеживать идентичность. Каждый раз, когда изменяется существующий элемент, источник данных для diffable видит изменение как удаление старого элемента и вставку нового элемента. В результате представление коллекции теряет важное состояние, связанное с элементом. Например, выбранный элемент становится невыбранным, когда изменяется какое-либо свойство элемента, потому что с точки зрения источника данных, доступного для сравнения, приложение удалило элемент и добавило новый на его место.
Кроме того, если animatingDifferences имеет значение true при применении моментального снимка, каждое изменение требует процесса анимации из старой ячейки и анимации в новой ячейке, что может отрицательно сказаться на производительности и привести к потере состояния пользовательского интерфейса, включая анимацию, внутри ячейки.
Кроме того, эта стратегия исключает использование методов reconfigureItems( :) или reloadItems( :) при заполнении моментального снимка структурами данных, поскольку эти методы требуют использования правильных идентификаторов. Единственным механизмом обновления данных для существующих элементов является применение нового моментального снимка, содержащего новые структуры данных, что приводит к тому, что diffable источник данных выполняет удаление и вставку для каждого измененного элемента.
Хранение структур данных непосредственно в различимых источниках данных и моментальных снимках не является надежным решением для многих реальных случаев использования, поскольку источник данных теряет способность отслеживать идентичность. Используйте этот подход только для простых случаев использования, в которых элементы не меняются, например элементы боковой панели в этом примере, или когда идентификатор элемента не важен. Для всех других случаев использования или если вы сомневаетесь в том, какой подход использовать, заполните diffable источники данных и моментальные снимки соответствующими идентификаторами.
Я немного смущен вашим последним предложением: вы пишете, что мой элемент представляет собой перечисление со связанными значениями ссылочного типа, но в вашем примере выше вы используетеstruct Book
, который является типом значения. Независимо от этого, в любом случае необходимо иметь в виду следующее:
Хеширование - это все об идентичности "объекта". Это просто своего рода ярлык для улучшения сравнения идентичностей, сворачивания и т. Д.
Если вы предоставляете собственную реализацию хеширования, два объекта a
а также b
должен вести себя так, чтобы a == b
подразумевает, что также hash(a) == hash(b)
(Почти всегда верно и обратное, но могут быть коллизии - особенно со слабыми алгоритмами хеширования - когда это не так).
Итак, если вы только хешируете title
а также author
, то вам нужно реализовать оператор сравнения таким образом, чтобы он также сравнивал title
а также author
. Тогда, еслиnotes
изменения, ни источник данных, ни какой-либо другой орган вообще не обнаруживают изменения идентичности.
UITableViewDiffableDataSource
это средство для облегчения синхронизации операторов вставки / удаления между представлением и источником данных. Если ты когда-нибудь получишь это
*** Завершение работы приложения из-за неперехваченного исключения "NSInternalInconsistencyException", причина: "Недействительное обновление: недопустимое количество разделов. Количество секций, содержащихся в табличном представлении после обновления (3), должно быть равно количеству секций, содержащихся в табличном представлении до обновления (3), плюс или минус количество вставленных или удаленных секций (0 вставлено, 2 удалено).
тогда источник данных - ваш друг.
Надеюсь, это немного поможет.