Mason Ballowe
Mason Ballowe

Reputation: 1866

tableView Cell Labels Stack When I Scroll

I have a custom tableView, entirely created in code. IE, the cells need to be in code too.

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "inventoryCell", for: indexPath)


            let namelabel = UILabel()
            namelabel.frame = CGRect(x: 145, y: 10 , width: 200, height: 30 )
            namelabel.text = itemToShow.name
            namelabel.font = UIFont.boldSystemFont(ofSize: 20)
            namelabel.backgroundColor = .clear
            cell.addSubview(namelabel)

            let detailLabel = UILabel()
            detailLabel.frame = CGRect(x: 145, y: 50 , width: 200, height: 50 )
            detailLabel.text = itemToShow.detail
            detailLabel.backgroundColor = .clear
            cell.addSubview(detailLabel)

            let inventoryImage = UIImageView()
            inventoryImage.frame = CGRect(x: 10, y: 10, width: 130, height: 130)
            inventoryImage.image = UIImage(named: "emptyInventorySlot")
            cell.addSubview(inventoryImage)

        
        
        return cell
    }

It works great, and you can see it loads perfectly. The top image is the load, the bottom image is once I scroll to the bottom. You can see the text labels seem to all stack on top of each other.

enter image description here

Upvotes: 1

Views: 306

Answers (1)

mcscxv
mcscxv

Reputation: 134

I would not recommend doing it this way. It's better to move the code that creates namelabel, detailLabel and inventoryImage to an inventoryCell class derived from UITableViewCell.

But you can make it work. Since table cells are reused and the reused cells already contain the created subviews, you need to either remove the subviews or treat cells with already created subviews differently.

And you should place your labels in the contentView of your UITableCellView.

To make your code work you can do this:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "inventoryCell", for: indexPath)

        // remove any subviews from contentView from recycled cells 
        cell.contentView.subviews.forEach{ $0.removeFromSuperview() }

        let namelabel = UILabel()
        namelabel.frame = CGRect(x: 145, y: 10 , width: 200, height: 30 )
        namelabel.text = itemToShow.name
        namelabel.font = UIFont.boldSystemFont(ofSize: 20)
        namelabel.backgroundColor = .clear
        // add any label to contentView of the cell
        cell.contentView.addSubview(namelabel)

        let detailLabel = UILabel()
        detailLabel.frame = CGRect(x: 145, y: 50 , width: 200, height: 50 )
        detailLabel.text = itemToShow.detail
        detailLabel.backgroundColor = .clear
        // add any label to contentView of the cell
        cell.contentView.addSubview(detailLabel)

        let inventoryImage = UIImageView()
        inventoryImage.frame = CGRect(x: 10, y: 10, width: 130, height: 130)
        inventoryImage.image = UIImage(named: "emptyInventorySlot")
        // add any label to contentView of the cell
        cell.contentView.addSubview(inventoryImage)

    
    
    return cell
}

Better would be:

  • create a UITableViewCell subclass
  • place your label placement and configuration there
  • take care of cell reuses, override prepareForReuse() to support cell reuse
  • register this class as a table view cell for your tableView
  • cast your dequeued cell to your UITableViewCell subclass

Upvotes: 1

Related Questions