如何实现两个独立类的UICollectionView间的委托通信?
实现两个独立UICollectionView的联动更新
先说结论:完全可行!
UICollectionView自带的UICollectionViewDelegate和你自定义的委托协议根本不冲突——系统委托管的是CollectionView自身的交互逻辑(比如选中、滚动事件),自定义委托是用来在两个独立ViewController之间传递选中信息的,两者各司其职,完全可以共存。
具体实现方法
方法一:自定义委托协议(最规范的组件通信方式)
适合明确的一对一或一对多交互场景,代码结构清晰,后续维护方便。
- 在第一个CollectionView所属的ViewController(比如叫
FirstCollectionVC)里定义自定义委托协议:
// FirstCollectionVC.swift import UIKit // 自定义委托协议,根据需求定义参数 protocol FirstCollectionSelectionDelegate: AnyObject { func didSelectItem(withType type: String) } class FirstCollectionVC: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource { // 用weak修饰避免循环引用 weak var selectionDelegate: FirstCollectionSelectionDelegate? // ... 其他初始化、数据源方法省略 public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let selectedType = viewModel.type[indexPath.row] // 通过自定义委托传递选中信息 selectionDelegate?.didSelectItem(withType: selectedType) } }
- 让第二个CollectionView的ViewController(比如
SecondCollectionVC)遵守该协议并实现方法:
// SecondCollectionVC.swift import UIKit class SecondCollectionVC: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, FirstCollectionSelectionDelegate { // ... 你的CollectionView、ViewModel等属性 func didSelectItem(withType type: String) { // 根据选中类型请求数据并刷新 getThings(url: type) { [weak self] in DispatchQueue.main.async { self?.uiCollectionViewTwo.reloadData() } } } // ... 数据源方法、getThings方法省略 }
- 在创建两个ViewController的地方(如父VC或导航控制器)绑定委托关系:
// 示例:在父VC中创建子VC let firstVC = FirstCollectionVC() let secondVC = SecondCollectionVC() firstVC.selectionDelegate = secondVC
方法二:闭包回调(简单场景首选)
如果只是一对一的简单通信,闭包比委托更简洁,无需额外定义协议。
- 在
FirstCollectionVC中添加闭包属性:
// FirstCollectionVC.swift class FirstCollectionVC: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource { // 闭包参数按需定义 var onItemSelected: ((String) -> Void)? public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let selectedType = viewModel.type[indexPath.row] // 触发闭包传递选中信息 onItemSelected?(selectedType) } }
- 创建
FirstCollectionVC时直接设置闭包逻辑:
let firstVC = FirstCollectionVC() let secondVC = SecondCollectionVC() firstVC.onItemSelected = { [weak secondVC] selectedType in secondVC?.getThings(url: selectedType) { DispatchQueue.main.async { secondVC?.uiCollectionViewTwo.reloadData() } } }
方法三:通知中心(适合一对多场景)
如果需要多个ViewController响应同一个选中事件,通知中心无需显式持有对象引用,但要注意移除通知防止内存泄漏。
- 在
FirstCollectionVC的选中方法中发送通知:
public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let selectedType = viewModel.type[indexPath.row] NotificationCenter.default.post( name: NSNotification.Name("FirstCollectionItemSelected"), object: nil, userInfo: ["selectedType": selectedType] ) }
- 在
SecondCollectionVC中监听通知:
override func viewDidLoad() { super.viewDidLoad() NotificationCenter.default.addObserver( self, selector: #selector(handleItemSelected(_:)), name: NSNotification.Name("FirstCollectionItemSelected"), object: nil ) } @objc func handleItemSelected(_ notification: Notification) { guard let selectedType = notification.userInfo?["selectedType"] as? String else { return } getThings(url: selectedType) { [weak self] in DispatchQueue.main.async { self?.uiCollectionViewTwo.reloadData() } } } // 销毁时移除通知 deinit { NotificationCenter.default.removeObserver(self) }
方法四:共享ViewModel(MVVM架构最优解)
如果项目采用MVVM模式,让两个ViewController共享同一个ViewModel实例是最优雅的方式——第一个VC更新ViewModel的选中状态,第二个VC监听变化自动刷新。
// 共享ViewModel,用Combine实现响应式 import Combine class SharedCollectionViewModel { @Published var selectedType: String? // 数据请求方法 func fetchData(for type: String) async throws { // 这里写网络请求或本地数据获取逻辑 } } // FirstCollectionVC class FirstCollectionVC: UIViewController { let viewModel: SharedCollectionViewModel init(viewModel: SharedCollectionViewModel) { self.viewModel = viewModel super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { viewModel.selectedType = viewModel.type[indexPath.row] } } // SecondCollectionVC class SecondCollectionVC: UIViewController { let viewModel: SharedCollectionViewModel private var cancellables = Set<AnyCancellable>() init(viewModel: SharedCollectionViewModel) { self.viewModel = viewModel super.init(nibName: nil, bundle: nil) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() viewModel.$selectedType .compactMap { $0 } .sink { [weak self] selectedType in Task { try await self?.viewModel.fetchData(for: selectedType) DispatchQueue.main.async { self?.uiCollectionViewTwo.reloadData() } } } .store(in: &cancellables) } } // 创建时传入同一个ViewModel实例 let sharedVM = SharedCollectionViewModel() let firstVC = FirstCollectionVC(viewModel: sharedVM) let secondVC = SecondCollectionVC(viewModel: sharedVM)
说说你现有代码的问题
你直接在FirstCollectionVC里调用self.uiCollectionViewTwo.reloadData()行不通——两个ViewController是独立的,直接持有对方的CollectionView会导致代码耦合度极高,后续维护和改需求会非常麻烦。上面的几种方法都能避免这种耦合,同时实现联动更新。
内容的提问来源于stack exchange,提问作者llama_glama
相关产品推荐
相关产品推荐

