UICollectionView中输入时UISearchBar频繁失去第一响应者问题
问题解决:UICollectionView头部搜索栏失焦与快照应用崩溃
问题根源
- 搜索栏失焦:每次更新
filteredWords触发dataSource.apply(snapshot)时,UICollectionView会重新生成头部补充视图,原搜索栏被销毁或复用,导致失去第一响应者状态。 - 队列崩溃:移除
DispatchQueue.main.async后,filteredWords的设置可能在非主队列执行,而UICollectionViewDiffableDataSource要求必须在主队列应用快照,因此触发崩溃。
修复方案
核心思路
确保快照始终在主队列应用,同时保留搜索栏的引用,避免视图复用导致焦点丢失。
1. 在ViewController中添加搜索栏引用
class ViewController: UIViewController { let words = ["foo", "bar"] var filteredWords = ["foo", "bar"] { didSet { DispatchQueue.main.async { [weak self] in guard let self else { return } self.dataSource.apply(self.snapshot, animatingDifferences: false) // 恢复搜索栏焦点 self.searchBar?.becomeFirstResponder() } } } var collectionView: UICollectionView! var dataSource: UICollectionViewDiffableDataSource<String, String>! private var searchBar: UISearchBar? // 新增搜索栏引用 // ... 原有代码 }
2. 修改头部注册逻辑,保存搜索栏引用
func configureDataSource() { // ... 原有代码 let searchbarHeaderRegistration = UICollectionView.SupplementaryRegistration<SearchBarCell>(elementKind: UICollectionView.elementKindSectionHeader) { [weak self] cell, _, _ in self?.searchBar = cell.searchBar cell.searchBar.delegate = self } // ... 原有代码 }
3. 移除delegate方法中的冗余异步包裹
extension ViewController: UISearchBarDelegate { func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { filteredWords = words.filter { $0.hasPrefix(searchText) } } func searchBarTextDidEndEditing(_ searchBar: UISearchBar) { filteredWords = words } }
额外优化建议
- 始终使用
animatingDifferences: false更新快照,减少视图复用频率,降低焦点丢失概率。 - 如果需要保留动画效果,可以在更新快照前记录搜索栏的当前文本和焦点状态,更新后恢复。
内容的提问来源于stack exchange,提问作者Filippo
相关产品推荐
相关产品推荐

