I'm implementing a search screen in my app using UITableViewDiffableDataSource
. Each cell represents a search hit and highlights the search match in the cell title, kind of like Xcode's Open Quickly window highlights portions of its result items. As text is typed into the search field, I update the results list. Results move up and down in the list as their relevance changes.
The trick is that I need to force every cell to re-render every time the search text changes, because a new search string means an update to the highlighted portions of the cell title. But I don't want to animate a deletion and insert, because it's still the same item. How can I tell the data source using the snapshot that it needs to reload cells?
I declare the data source like this:
@property (retain) UITableViewDiffableDataSource<NSString *, SearchHit *> *dataSource;
SearchHit
represents one search result; it has properties for a display title and an array of ranges to highlight in the title. And it overrides hash
and isEqual:
so that every result row is uniquely identified.
My code looks something like this:
-(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];
}
At first I didn't have the reloadItemsWithIdentifiers
call there, and then no cell would change at all once it was in the result list. Adding the reload
call helped, but now most of the cells are constantly one update behind. This smells like a logic error somewhere in my code, but I've verified that the hits passed to the snapshot are correct and the hits passed to the data source's cell creation callback are not.
This article by Donny Wals and this related Twitter thread involving Steve Breen suggests that the way to fix this is to make the item identifier type only represent the properties needed to display the cell. So I updated SearchHit
's hash and equality comparison to include the highlighted portions of the title, which they didn't before. Then I got delete and insert animations for all the cells on every update, which I don't want.
This seems like what reloadItemsWithIdentifiers
should do...right?
Sample project here on GitHub.
...animatingDifferences: NO]
? β EdanaanimatingDifferences
parameter seems to indicate that the animation is not the whole point π β Edana[self.tableView reloadData]
. If I recall correctly from the WWDC talk where it was introduced, theanimatingDifferences
parameter exists at least partly to avoid animating the initial appearance of data when a view is loaded, and other such edge cases? β Mordent