如何通过代理模式从另一个类更新UICollectionView?
问题根源分析
你遇到的nil错误核心原因是你在选中User项时,新建了一个SavedInterestsViewController实例并赋值给代理,而这个新创建的控制器的view(包括collectionView outlet)还没有被加载,所以collectionView是nil,调用reloadData()就会崩溃。代理模式的核心是要引用已经存在的、处于导航栈中的SavedInterestsViewController实例,而不是新建一个。
步骤1:正确设置代理关系
当你从SavedInterestsViewController跳转到ViewController时(比如通过addButtonPressed的segue),在跳转前把ViewController的delegate设置为当前的SavedInterestsViewController实例:
修改SavedInterestsViewController,添加segue准备方法:
class SavedInterestsViewController: UIViewController, ViewControllerDelegate { // ... 现有代码 ... override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "SavedToNew" { let destinationVC = segue.destination as! ViewController // 将当前控制器实例设为代理 destinationVC.delegate = self } } // ... 现有代码 ... }
然后删除ViewController中didSelectItemAt里新建代理的错误代码,直接调用已有代理方法:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { if userArray[indexPath.item].checked == false { userArray[indexPath.item].checked = true collectionView.reloadData() let newUser = User() newUser.image = userArray[indexPath.item].image newUser.name = userArray[indexPath.item].name newUser.checked = true // 替换try!为do-catch处理错误 do { try realm.write { realm.add(newUser) } // 调用代理方法通知刷新 self.delegate?.didUpdate(sender: self) } catch { print("保存到Realm失败: \(error)") } } else { userArray[indexPath.item].checked = false collectionView.reloadData() } }
步骤2:优化SavedInterestsViewController的数据刷新
现在代理调用didUpdate时,需要重新从Realm获取最新数据,而不是只在viewDidLoad加载一次。同时推荐使用Realm的通知机制自动监听数据变化:
class SavedInterestsViewController: UIViewController, ViewControllerDelegate { // ... 现有代码 ... func didUpdate(sender: ViewController) { DispatchQueue.main.async { self.fetchDataFromRealm() // 重新获取最新数据 } } func fetchDataFromRealm() { // 先清空旧数据,再添加最新的Realm数据 userArray.removeAll() userArray.append(contentsOf: realm.objects(User.self)) collectionView.reloadData() } override func viewDidLoad() { super.viewDidLoad() collectionView.delegate = self collectionView.dataSource = self collectionView.register(UINib(nibName: "CollectionViewCell", bundle: nil), forCellWithReuseIdentifier: "CollectionCell") fetchDataFromRealm() // 添加Realm数据变化通知,自动刷新 NotificationCenter.default.addObserver( self, selector: #selector(realmDataChanged), name: NSNotification.Name.RealmObjectsDidChange, object: realm.objects(User.self) ) } @objc func realmDataChanged() { DispatchQueue.main.async { self.fetchDataFromRealm() } } // 页面消失时移除通知,避免内存泄漏 override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) NotificationCenter.default.removeObserver(self) } // ... 现有代码 ... }
步骤3:修复潜在的其他问题
- 避免强制try!:把所有
try!替换为do-catch,防止Realm操作失败导致崩溃。 - 优化网络请求刷新逻辑:在
ViewController的搜索方法中,用DispatchGroup确保所有异步请求完成后再刷新collectionView:
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { print("One second...") let functionClass = NetworkingFunctions() guard let searchText = searchBar.text else { return } functionClass.getWikipediaAssumptions(for: searchText) { [self] (articleArray) in self.userArray.removeAll() let group = DispatchGroup() for x in articleArray { group.enter() functionClass.performWikipediaSearch(with: x, language: WikipediaLanguage("en")) { (user) in self.userArray.append(user) group.leave() } } // 所有请求完成后统一刷新 group.notify(queue: .main) { self.collectionView.reloadData() } } }
总结
通过以上修改,你解决了代理实例错误的核心问题,确保代理引用的是已经加载完成的SavedInterestsViewController实例,同时优化了数据刷新逻辑,保证UICollectionView能正确展示最新的Realm数据。
内容的提问来源于stack exchange,提问作者yoKurt
相关产品推荐
相关产品推荐

