iosswiftuiviewuicollectionviewuicollectionviewdelegate

Adding UICollectionView inside UIView without Storyboards


I have ViewController called myVC with UITablewView - myTable.

What I want is to add some UIView as myTable's headerView from code. So inside viewDidLoad() method of myVC I added this code

    let topView = TopView()
    topView.frame.size.height = 100
    topView.frame.size.width = myTable.frame.width
    myTable.tableHeaderView = featuredEventsView

I also created file called TopView.swift that looks something like

class TopView : UIView {
    override init(frame: CGRect) {
        super.init(frame: frame)            
        self.backgroundColor = .red
    }

    required init?(coder aDecoder: NSCoder) {.....}
}

And it is working as it should. I see red UIView in headerView of myTable.

Now I want to add UICollectionView inside topView and I have problems here. I am trying to do something like

class TopView : UIView, UICollectionViewDataSource, UICollectionViewDelegate {
    override init(frame: CGRect) {
        super.init(frame: frame)            
        self.backgroundColor = .red

        addSubview(myCollectionView)
    }

    required init?(coder aDecoder: NSCoder) {.....}

let myCollectionView : UICollectionView = {
        let cv = UICollectionView()
        cv.translatesAutoresizingMaskIntoConstraints = false
        cv.delegate = self as! UICollectionViewDelegate
        cv.dataSource = self as! UICollectionViewDataSource
        cv.backgroundColor = .yellow
        return cv
    }()
}

I also created functions needed to UICollectionViewDataSource but app crashes after building. What am I doing wrong?


Solution

  • You have two problems:

    1) You initialise your UICollectionView incorrectly as you must give it a layout. You need something like this (use whatever frame you want but if you are going on to use auto layout it doesn't matter):

    let layout = UICollectionViewFlowLayout()
    let cv = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)
    

    2) You cannot reference 'self' inside the closure when initialising a property. This is because if may not have been initialised (as in this case) so you can't guarantee it's safe to use it.

    I think you should be ok if you use lazy initialisation like this (plus you don't even need to cast 'self'):

    lazy var myCollectionView : UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        let cv = UICollectionView(frame: CGRect.zero, collectionViewLayout: layout)
        cv.translatesAutoresizingMaskIntoConstraints = false
        cv.delegate = self
        cv.dataSource = self
        cv.backgroundColor = .yellow
        return cv
    }()
    

    Using the lazy method should delay until self is initialised and therefore safe to use.