Rakesha Shastri
Rakesha Shastri

Reputation: 11242

UITableViewCell with gradient not working until first reuse of cell

I want to add a gradient to part of a custom UITableViewCell. This is my gradient Code.

func addGradient() {
    gradient = CAGradientLayer()
    gradient.frame = CGRect(x: 0, y: 0, width: gradientView.frame.width, height: gradientView.frame.height)
    gradient.colors = [
        UIColor(red:0.23, green:0.24, blue:0.28, alpha:1).cgColor,
        UIColor(red:0.11, green:0.11, blue:0.12, alpha:0.6).cgColor
    ]
    gradient.locations = [0, 1]
    gradient.startPoint = CGPoint(x: 0.5, y: 0)
    gradient.endPoint = CGPoint(x: 0.5, y: 1)
    gradientView.layer.addSublayer(gradient)
}

This is my gradientView code.

func addGradientView() {
    containerView.addSubview(gradientView)

    gradientView.translatesAutoresizingMaskIntoConstraints = false
    gradientView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
    gradientView.topAnchor.constraint(equalTo: barChart.bottomAnchor).isActive = true
    gradientView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
    gradientView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true
}

addGradient() is called in the layoutSubiviews() method. But the real height and width don't seem to be reflecting until the first reuse of the cell.

Upvotes: 1

Views: 2343

Answers (3)

nitin.agam
nitin.agam

Reputation: 2142

Create a global variable inside the cell.

private let gradientLayer: CAGradientLayer = {
    let layer = CAGradientLayer()
    layer.colors = [UIColor.magenta.cgColor, UIColor.black.cgColor]
    layer.locations = [0.0, 1.0]
    layer.cornerRadius = 5
    layer.masksToBounds = true
    return layer
}()

Then add this gradient layer in the initialize method where you are adding UI components.

Now you have to update the frame of the gradient layer.

override func layoutSubviews() {
    super.layoutSubviews()
    updateGradientFrame()
}

private func updateGradientFrame() {
    gradientLayer.frame = // set your frame here...
}

Upvotes: 0

SWAT
SWAT

Reputation: 1158

Your TableViewCell does not have a frame yet. You may print the frame to check that, it will be a zero frame.

Move the addGradientView method to the layoutSubView override.

override func layoutSubviews() {
    super.layoutSubviews()
    addGradient()
}

Edit:

I see that you have mentioned it didn't work in layoutSubView. I believe there is some problem in the way you are calling the method. The below code works for me

class GradientCell: UITableViewCell {


override func layoutSubviews() {
    super.layoutSubviews()
    print(self.frame)
    addGradient()
}

func addGradient() {
    let gradient = CAGradientLayer()
    gradient.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height)
    gradient.colors = [
        UIColor(red:0.23, green:0.24, blue:0.28, alpha:1).cgColor,
        UIColor(red:0.11, green:0.11, blue:0.12, alpha:0.6).cgColor
    ]
    gradient.locations = [0, 1]
    gradient.startPoint = CGPoint(x: 0.5, y: 0)
    gradient.endPoint = CGPoint(x: 0.5, y: 1)
    self.layer.addSublayer(gradient)
}

}

enter image description here

EDIT 2: It would be better if you move the didMoveToWindow

Upvotes: 2

Au Ris
Au Ris

Reputation: 4669

Option 1: Call your addGradient() from tableview's willDisplayCell delegate method.

public func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    if cell is YourCustomTableViewCell {
        (cell as! YourCustomTableViewCell).addGradient()
    }
}

Make sure you add the gradient once. Because of the cell reuse addGradient() may get called several times on the same cell. So better rename your function to addGradientfNeeded() and adjust its logic accordingly.

Option 2:

Instead of adding your gradient in willDisplay method, add the gradient view and the layer in the cell (once) and only update the frame of the gradient layer.

public func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    if cell is YourCustomTableViewCell {
        (cell as! YourCustomTableViewCell).updateGradientLayerFrame()
    }
}

Option 3:

Create a GradientView class and add it either in the interface builder or programatically to your cell, the gradient will resize as the view's frame changes:

public class GradientView: UIView {
    private var gradientLayer: CAGradientLayer?

    override public func layoutSubviews() {
        super.layoutSubviews()

        guard gradientLayer == nil else {
            gradientLayer?.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height)
            return
        }

        gradientLayer = CAGradientLayer()
        gradientLayer!.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height)
        gradientLayer!.colors = [
            UIColor(red:0.23, green:0.24, blue:0.28, alpha:1).cgColor,
            UIColor(red:0.11, green:0.11, blue:0.12, alpha:0.6).cgColor
        ]
        gradientLayer!.locations = [0, 1]
        gradientLayer!.startPoint = CGPoint(x: 0.5, y: 0)
        gradientLayer!.endPoint = CGPoint(x: 0.5, y: 1)
        self.layer.addSublayer(gradientLayer!)
    }
}

Upvotes: 3

Related Questions