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

如何使UIViewController成为UITableViewCell中UICollectionView的代理以契合MVC架构

Great question! When dealing with nested collection views inside table view cells, keeping your MVC architecture clean means making sure your view controller handles the business logic and interactions, while the cells stay focused solely on displaying content. Here's a step-by-step approach to make your view controller act as the collection view's handler using a custom delegate pattern—this keeps everything aligned with MVC principles:

Step 1: Create a Custom Delegate Protocol for Your Table View Cell

First, define a protocol in your table view cell class. This protocol will act as a bridge to pass collection view events and data requests up to your view controller. It should declare the methods you need (like handling selection, providing data counts, or configuring collection view cells).

import UIKit

// Protocol to bridge collection view interactions to the view controller
protocol NestedCollectionCellDelegate: AnyObject {
    // Ask for the number of items in the collection view for a specific cell
    func numberOfItemsInCollection(for cell: YourTableViewCell) -> Int
    // Provide data for a specific collection view item
    func dataForCollectionItem(at indexPath: IndexPath, in cell: YourTableViewCell) -> YourDataModel?
    // Handle selection of a collection view item
    func collectionItemSelected(at indexPath: IndexPath, in cell: YourTableViewCell)
}

class YourTableViewCell: UITableViewCell {
    // Weak reference to avoid retain cycles (critical for MVC memory management)
    weak var delegate: NestedCollectionCellDelegate?
    
    @IBOutlet weak var collectionView: UICollectionView!
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // Set the collection view's dataSource and delegate to the cell itself
        collectionView.dataSource = self
        collectionView.delegate = self
        // Register your collection view cell if you haven't done it in IB
        collectionView.register(UINib(nibName: "YourCollectionCell", bundle: nil), forCellWithReuseIdentifier: "YourCollectionCell")
    }
}

Step 2: Make the Table View Cell Forward Collection View Calls to Its Delegate

Extend your table view cell to conform to UICollectionViewDataSource and UICollectionViewDelegate. In these methods, instead of handling logic directly, you'll call the corresponding methods on your custom delegate (which will be your view controller).

extension YourTableViewCell: UICollectionViewDataSource, UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        // Delegate the count request to the view controller
        return delegate?.numberOfItemsInCollection(for: self) ?? 0
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "YourCollectionCell", for: indexPath) as! YourCollectionCell
        
        // Get data from the view controller and configure the collection cell
        if let data = delegate?.dataForCollectionItem(at: indexPath, in: self) {
            cell.configure(with: data) // Assume you have a configure method on your collection cell
        }
        
        return cell
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        // Forward the selection event to the view controller
        delegate?.collectionItemSelected(at: indexPath, in: self)
    }
}

Step 3: Have Your View Controller Conform to the Custom Delegate Protocol

Now, in your view controller, adopt the NestedCollectionCellDelegate protocol. Here, you'll implement the methods to provide data and handle interactions—this is where your MVC controller logic lives.

First, make sure your view controller has a data source (like an array of sections, each containing items for the collection view):

class YourViewController: UIViewController {
    @IBOutlet weak var tableView: UITableView!
    
    // Example data structure: each section has a collection of items
    var sectionData: [[YourDataModel]] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.dataSource = self
        tableView.delegate = self
        tableView.register(UINib(nibName: "YourTableViewCell", bundle: nil), forCellReuseIdentifier: "YourTableViewCell")
        // Load your data here
        loadSectionData()
    }
    
    private func loadSectionData() {
        // Populate sectionData with your actual data
    }
}

Then, extend the view controller to conform to the custom delegate and implement the required methods:

extension YourViewController: NestedCollectionCellDelegate {
    func numberOfItemsInCollection(for cell: YourTableViewCell) -> Int {
        // Find the table view index path for the cell to get the correct section data
        guard let indexPath = tableView.indexPath(for: cell) else { return 0 }
        return sectionData[indexPath.row].count
    }
    
    func dataForCollectionItem(at indexPath: IndexPath, in cell: YourTableViewCell) -> YourDataModel? {
        guard let cellIndexPath = tableView.indexPath(for: cell) else { return nil }
        return sectionData[cellIndexPath.row][indexPath.item]
    }
    
    func collectionItemSelected(at indexPath: IndexPath, in cell: YourTableViewCell) {
        // Handle the selection logic here (MVC controller responsibility)
        guard let cellIndexPath = tableView.indexPath(for: cell) else { return }
        let selectedItem = sectionData[cellIndexPath.row][indexPath.item]
        
        // Example: Push a detail view controller
        let detailVC = DetailViewController()
        detailVC.data = selectedItem
        navigationController?.pushViewController(detailVC, animated: true)
    }
}

Step 4: Connect the Delegate in cellForRowAt

Finally, when dequeuing your table view cell, set the view controller as the cell's delegate:

extension YourViewController: UITableViewDataSource, UITableViewDelegate {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return sectionData.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withReuseIdentifier: "YourTableViewCell", for: indexPath) as! YourTableViewCell
        // Set the delegate to self (the view controller)
        cell.delegate = self
        // Optional: Configure any table cell-specific UI here
        return cell
    }
}

Why This Works for MVC

  • View Layer: Your table view cell and collection view cell are pure views—they don't hold business logic, just forward events and display data provided by the controller.
  • Controller Layer: Your view controller handles all data fetching, business logic, and user interactions—exactly what MVC expects from a controller.
  • Model Layer: Your YourDataModel holds the raw data, separate from views and controllers.

If you're using Objective-C, the pattern is identical—just use @protocol with weak (or assign for older iOS versions) properties, and implement the delegate methods in your view controller.


内容的提问来源于stack exchange,提问作者Piyush Vaish

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:34:26