You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过代理模式从另一个类更新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:修复潜在的其他问题

  1. 避免强制try!:把所有try!替换为do-catch,防止Realm操作失败导致崩溃。
  2. 优化网络请求刷新逻辑:在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 11:23:00