Search code examples
iosswiftuicollectionviewreusability

How to reuse a Collection View? -Swift


In an application I'm trying to build I need a lot of UICollectionViews (around 10). I decided to make the collectionViews without the use of Storyboards (i.e entirely in code) . Storyboards complicate things(for many collectionViews) .

Here is the code:

I)

   override func viewDidLoad() {

    super.viewDidLoad()
        //Create the collection View      

    let frame =                  CGRect(origin: CGPoint.zero , size: CGSize(width: self.view.frame.width , height: 50))
    let layout =                 UICollectionViewFlowLayout()
     collectionView1 =           UICollectionView(frame: frame, collectionViewLayout: layout)
    collectionView1.dataSource = self
    collectionView1.delegate =   self
    collectionView1.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cellIdentifier")
    collectionView1.backgroundColor = UIColor.red
    view.addSubview(collectionView1) }

II)

 // TheData Source and Delegate 

extension ViewController : UICollectionViewDataSource , UICollectionViewDelegateFlowLayout{


public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {

 return 5

}

public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {


        let cell =              collectionView.dequeueReusableCell(withReuseIdentifier: "cellIdentifier", for: indexPath)
        cell.backgroundColor = UIColor.darkGray
        return cell

}


func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

    return CGSize(width: 50, height: 50)
}
 }

This would create one collectionView in a View Controller , however if I had to create ten more of these I would have to rewrite the above code 10 times .

So I tried to create a separate MyCollectionView class with the basic implementation needed to add a collectionView to a ViewController so that all I had to do in my view controller was something as simple as

 override func viewDidLoad() {

super.viewDidLoad()

 let shoesCollection = MYCollectionView(frame : ...)
 self.view.addSubview(shoesCollection)   

 let foodCollection = MYCollectionView(frame : ...)
 self.view.addSubview(foodCollection)

 let carCollection = MYCollectionView(frame : ...)
 self.view.addSubview(carCollection)   

 }

or something similar . However I was unsuccessful. How shall I go about this? Thanks!


Solution

  • I will just give you simple example although I'm not sure if this is what you wanted. Again, it totally depends on the design you are implementing but this could be one of the ideas.

    The idea is

    1. Create a collection view that will populate 10 collection views you want to add.

    2. Create a collection view cell that has the collection view you want to repeat.

    3. Feed different data(food, shoes, cars and so on) to the collection view cell (MYCollectionView).

    4. Feed each data to the collection view in the cell to populate the individual data.

    ViewController

    class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout  {
        lazy var collectionView: UICollectionView = {
            let layout = UICollectionViewFlowLayout()
            layout.minimumInteritemSpacing = 0
            layout.minimumLineSpacing = 10
            layout.scrollDirection = .vertical
    
            let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
            collectionView.delegate = self
            collectionView.dataSource = self
            collectionView.register(MYCollectionView.self, forCellWithReuseIdentifier: NSStringFromClass(MYCollectionView.self))
            collectionView.backgroundColor = .clear
            return collectionView
        }()
    
    
        override func viewDidLoad() {
            super.viewDidLoad()
            view.addSubview(collectionView)
        }
    
        override func viewWillLayoutSubviews() {
            super.viewWillLayoutSubviews()
            collectionView.frame = view.bounds
        }
    
        func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return 10
        }
    
        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: NSStringFromClass(MYCollectionView.self), for: indexPath) as! MYCollectionView
            return cell
        }
    
    
        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
            return CGSize(width: view.bounds.width, height: 100)
        }
    }
    

    MYViewController

    class MYCollectionView: UICollectionViewCell, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    
        lazy var collectionView: UICollectionView = {
            let layout = UICollectionViewFlowLayout()
            layout.scrollDirection = .horizontal
    
            let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
            collectionView.dataSource = self
            collectionView.delegate =   self
            collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "cellIdentifier")
            collectionView.backgroundColor = UIColor.red
            return collectionView
        }()
    
        override init(frame: CGRect) {
            super.init(frame: frame)
    
            contentView.addSubview(collectionView)
        }
    
        override func layoutSubviews() {
            super.layoutSubviews()
            collectionView.frame = bounds
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return 15
        }
    
        public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellIdentifier", for: indexPath)
            cell.backgroundColor = UIColor.darkGray
            return cell
    
        }
    
    
        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
            return CGSize(width: bounds.height, height: bounds.height)
        }
    
        func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
            print("Clicked")
        }
    }
    

    Updated: Since the question was only about reusing collection view I didn't really pass the data all the way to the reused collection views. Assuming that when the view controller is loaded you have the data, let say array of shoes, food and cars and also each array components are array too.

    For example,

    let shoes = [...] //array of shoes
    let foods = [...] //array of foods
    let cars = [...] //array of cars
    let data = [shoes, foods, cars]
    

    Now your data array has 3 components and this will decide number of the collection view you created in your question. So in the view controller in my example code,

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return data.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: NSStringFromClass(MYCollectionView.self), for: indexPath) as! MYCollectionView
        let components = data[indexPath.item]
        cell.data = components
        return cell
    }
    

    In MYCollectionView, you should have a variable, data.

    var data:[WhateverYourDataModel] = [] {
        didSet {
            collectionView.releadData()
        }
    }
    
    public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return data.count
    }
    
    public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellIdentifier", for: indexPath)
        cell.backgroundColor = UIColor.darkGray
    
        //let component = data[indexPath.item]
        //You should create a custom collection view cell to display whatever you want to display with the data, component. 
    
        return cell
    }
    

    To make it clean, your data object should have common base model so you can pass them from view controller all the way down to the collection view in the MYCollectionView.

    enter image description here