1

I have a collectionView with multiple cells. Each cell is of this class:

class OuterCollectionViewCell: UICollectionViewCell {

    @IBOutlet weak var stackView: UIStackView!

    override init(frame: CGRect) {
        super.init(frame: frame)
        print("initting")
        for i in 1...30 {
            let view = UIView()
            view.backgroundColor = UIColor.red
            view.tag = i

            self.stackView.addSubview(view)
        }
    }
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

}

I am creating each cell which I would hope calls its initializer like so:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "outerCell", for: indexPath) as! OuterCollectionViewCell
    cell.backgroundColor = UIColor.gray
    return cell
}

My intent is to add 30 subviews to this horizontal stackView such that 30 stripes show up. I want to add these subviews in programmatically in the init to avoid having to do it manually in the storyboard. However right now the init is not called and the views are not added. Any idea why this might be and how to do this?

BigBoy1337
  • 4,735
  • 16
  • 70
  • 138

2 Answers2

2

which I would hope calls its initializer like so

You can hope that, but, as you've rightly found, you are doomed to disappointment. A cell generated by calling dequeue never has its init(frame:) called.

One obvious solution is to add your 30 subviews right in cellForItemAt. If you do that, you also need to include a test to make sure that you don't add them again the next time the cell is reused. In other words, add them only if they are not already there.

Even simpler: since you've already got an implementation of init(coder:), why not move your 30-subviews code into that? That is an initializer that will be called when a cell is created through dequeuing, and it won't be called (obviously) when a cell is merely reused.

Note, however, that there are other problems with what you're doing. As your code stands, your views have no size, and it is far from clear what the stack view is going to make of that. Moreover, merely saying addSubview won't cause the stack view to do anything with your views. So basically even if your code runs, I would expect you won't actually see anything. However, let's cross one bridge at a time.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Thanks! This is exactly the type of feedback I need. So if I put the logic to add the 30 subviews in required init?(coder aDecoder: NSCoder) {, and I give them the proper size - Would I do that with layout constraints? Note that now I am using fill equally on the stackView. Also, I intend to color the sub-views selectively thus creating basically stripes. Eventually there will be 30 stripes, each as a day in a month. The month is represented by the collectionViewCell. – BigBoy1337 Jun 15 '18 at 00:47
  • 1
    Let's solve one problem at a time, please! Your problem was merely getting your code to run on cell creation. That's the question I answered, and you should confirm by experimentation that it's correct. My last paragraph was not meant to open a whole new can of worms, but to warn your that even after your code does run, it still won't actually work. If you want to ask a question about how to configure these subviews once your code is running, ask it as a separate question! – matt Jun 15 '18 at 00:57
0

Another potential problem here is if you want to add custom method variables to your initializer. For things like styling...

I ended up extracting all layout code into its own method that gets called after dequeueReusableCell.

let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! Cell
cell.ifNeeded(layout: .someStyle)

Then having a custom var you can keep track of initialization.

var layout:Layout?

And then exit if it's already been laid out.

func ifNeeded(layout:Layout) {
    //exit if already laid out
    guard self.layout == nil else { return }
    self.layout = layout
Andres Canella
  • 3,706
  • 1
  • 35
  • 47