UITableViewCell's layer not getting updated after heightForRowAt IndexPath

2.1k views Asked by At

I am changing the size of each TableViewCell with:

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return 60
}

Then, within the custom cell class, I am inserting a 'gradientLayer' as a sublayer to the Cell's layer property:

override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
    ...
    gradientLayer.frame = self.layer.bounds
    layer.insertSublayer(gradientLayer, at: 0)
    ...
}

However, the gradient is not filling up the whole cell: enter image description here

Does anyone know why this is happening/how I can fix it?

Thank you.

3

There are 3 answers

19
AudioBubble On BEST ANSWER

If you use a plain UITableViewCell :

 override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 100.0
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath)

        // ...

        let gradient =  CAGradientLayer()
        gradient.frame = cell.contentView.frame
        gradient.colors = [UIColor.blue.cgColor, UIColor.yellow.cgColor]
        gradient.locations = [0.0, 1.0]
        cell.contentView.layer.insertSublayer(gradient, at: 0)

        // ...

        return cell
    }

You need to make sure that you're adding the gradient to the contentView's layer.

And this would work just fine.

If you use a custom UITableViewCell class :

Now, if you are using a custom cell class, you will need to override the layoutSubviews() method like so :

override func layoutSubviews() {
    super.layoutSubviews()

    gradient.frame = contentView.frame
}

This ensures that your gradient frame is always equal to your contentView frame at anytime.

Your custom cell class would then look like this:

class CustomTableCell: UITableViewCell {

    lazy var gradient : CAGradientLayer = {
        var gradient = CAGradientLayer()
        gradient.colors = [UIColor.blue.cgColor, UIColor.yellow.cgColor]
        gradient.locations = [0.0, 1.0]
        self.contentView.layer.insertSublayer(gradient, at: 0)
        return gradient
    }()

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        // ...

    }

    override func layoutSubviews() {
        super.layoutSubviews()

        // ...

        gradient.frame = contentView.frame

        // ...

    }

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

        // ...
    }


}

Just let me know if you have any additional questions !

0
clarus On

The frame size at init time is the default frame of your cell, from the storyboard or wherever. It won't have your height of 60 (unless that matches) because the cell is just being created in init and then it will be resized.

You can add the layer there, but do the resizing in layoutSubviews or use constraints to autolayout. If you do that, the gradient will fill your cell at its final size.

1
Shades On

Why is it happening? I don't know.

But if you put the gradient inside your tableview's cellForRowAt, you get:

enter image description here


Here's the code:

override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return 60
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{

    let cell =  tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)

    let gradient: CAGradientLayer = CAGradientLayer()
    gradient.colors = [UIColor.blue.cgColor, UIColor.red.cgColor]
    gradient.frame = CGRect(x: 0.0, y: 0.0, width: cell.frame.size.width, height: cell.frame.size.height)
    cell.layer.insertSublayer(gradient, at: 0)

    return cell
}