Search code examples
iosobjective-cswiftuitableviewaddsubview

How cells are reused in UITableView?


I think that I am missing something in a way that cells are reused in my UITableView.

I have ViewController with TableView and custom cells with their own class and identifier. In cellForRowAt method I use

let cell: MyCell = myTableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath) as! MyCell
    cell.objectForCell = myObject

And it works as it should, I got TableView with cells. I also have object that i pass to my cell. And inside cell I have this code

var objectForCell : ObjectClass? {
    didSet {
        if let object = objectForCell {
            someLabel.text = object.title
            ....

That also works fine. But what if I want to add some subviews to cells? For example let it be each 3rd cell. When TableView is presented I see my subview in each 3rd cell as I expect but why I got this subview in every cell when I start to scroll in different directions?

var objectForCell : ObjectClass? {
    didSet {
        if let object = objectForCell {
            someLabel.text = object.title
            if object.id % 3 == 0 {
               self.addSubview(......)
            }

Solution

  • cellForRowAt will return an existing cell object if there is one that is no more used by the table view. If that cell was a third one when it was presented, it has that new subview added. So to make it work as you expect, you would have to remove that subview in prepareForReuse which is called when that cell is being removed from view hierarchy and enqueued in the queue of reusable cells. That way when you call dequeueReusableCell, you will get a cell without the new subview.

    However, in general, you do not want to add/remove subviews in cellForRowAt (unless there is no other option). Rather, if it is only a single specific view that you want to show in every third cell, add it in initializer and hide/unhide it in cellForRowAt (in your case, in objectForCell).

    So declare a property:

    let specialSubview = UIView()
    

    Add it in init (or, if you use nib, then in awakeFromNib) and position it:

    self.addSubview(specialSubview)
    // autolayout constraints, configuring, etc.
    

    And then in objectForCell:

    var objectForCell : ObjectClass? {
        didSet {
            if let object = objectForCell {
                someLabel.text = object.title
                if object.id % 3 == 0 {
                    specialSubview.isHidden = false
                } else {
                    specialSubview.isHidden = true
                }
                ...