Как я могу перезагружать элементы без удаления и вставки с помощью UITableViewDiffableDataSource?
Я реализую экран поиска в своем приложении, используя UITableViewDiffableDataSource
. Каждая ячейка представляет результат поиска и выделяет совпадение при поиске в заголовке ячейки, вроде как окно Xcode Open Quickly выделяет части элементов результатов. По мере ввода текста в поле поиска я обновляю список результатов. Результаты перемещаются вверх и вниз по списку по мере изменения их релевантности.
Хитрость в том, что мне нужно заставить каждую ячейку повторно отображать каждый раз при изменении текста поиска, потому что новая строка поиска означает обновление выделенных частей заголовка ячейки. Но я не хочу анимировать удаление и вставку, потому что это все тот же элемент. Как я могу сообщить источнику данных с помощью снимка состояния, что ему необходимо перезагрузить ячейки?
Я объявляю источник данных так:
@property (retain) UITableViewDiffableDataSource<NSString *, SearchHit *> *dataSource;
SearchHit
представляет один результат поиска; у него есть свойства для отображаемого заголовка и массив диапазонов, которые нужно выделить в заголовке. И это отменяетhash
а также isEqual:
так что каждая строка результатов однозначно идентифицируется.
Мой код выглядит примерно так:
-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
NSArray<SearchHit *> *hits = [self fetchHits:searchText];
NSDiffableDataSourceSnapshot<NSString *, SearchHit *> *snap = [[[NSDiffableDataSourceSnapshot alloc] init] autorelease];
[snap appendSectionsWithIdentifiers:@[@""]];
[snap appendItemsWithIdentifiers:hits];
[snap reloadItemsWithIdentifiers:hits];
[self.dataSource applySnapshot:snap animatingDifferences:YES];
}
Сначала у меня не было reloadItemsWithIdentifiers
вызовите там, и тогда никакая ячейка не изменится вообще, как только она окажется в списке результатов. Добавлениеreload
звонок помог, но сейчас большинство ячеек постоянно на одно обновление отстают. Это пахнет логической ошибкой где-то в моем коде, но я проверил, что попадания, переданные в моментальный снимок, правильные, а попадания, переданные в обратный вызов создания ячейки источника данных - нет.
Эта статья Донни Уолса и связанная с ней ветка в Твиттере с участием Стива Брина предполагает, что способ исправить это - сделать так, чтобы тип идентификатора элемента представлял только свойства, необходимые для отображения ячейки. Итак, я обновилSearchHit
сравнение хэша и равенства, чтобы включить выделенные части заголовка, чего раньше не было. Затем мне нужно было удалять и вставлять анимацию для всех ячеек при каждом обновлении, чего я не хочу.
Это похоже на то, что reloadItemsWithIdentifiers
должен делать... верно?
Пример проекта здесь, на GitHub.
2 ответа
API источника данных diff может быть неподходящим инструментом для создания анимации самих ячеек. Он предназначен для анимации появления, исчезновения и упорядочения ячеек. Если в вашем источнике данных есть изменение, которое выражается в соответствии с Hashable, api увидит это как изменение и удалит / вставит и т. Д.
Я бы посоветовал удалить поисковый текст из идентификатора элемента и заставить каждую ячейку наблюдать за поисковым текстом и производить анимацию или перерисовывать независимо от источника данных.
Правильное решение этой проблемы на самом деле находится в именах API — объекты, которые вы передаете источнику данных, должны быть идентификаторами , такими как значения rowid из базы данных. В моем случае, когда идентификаторы элементов не представляют строки в базе данных, которые я могу найти, мне просто нужно сохранить состояние объектов в какой-то структуре поиска, чтобы при вызове
reloadItemsWithIdentifiers
, я получаю состояние каждой ячейки из этой структуры, а не из объекта, который передает мне источник данных.