Background: My app allows users to select a gradient border to apply to UITableViewCells that are dynamically sized based on the content within them. I am currently creating this border by inserting a CAGradientLayer sublayer to a UIView that sits within the cell.
Issue: Because each cell is sized differently, I am resizing the CAGradientLayer by overriding layoutIfNeeded in my custom cell class. This works, but seems suboptimal because the border is being redrawn over and over again and flickers as the cell is resizing.
Link to Screen Capture:
https://drive.google.com/file/d/1SiuNozyUM7LCdYImZoGCWeoeBKu2Ulcw/view?usp=sharing
Question: Do I need to take a different approach to creating this border? Or am I missing something regarding the UITableViewCell lifecycle? I have come across similar issues on SO, but none that seem to address this redraw issue. Thank you for your help.
CAGradientLayer Extension to Create Border
extension CAGradientLayer {
func createBorder(view: UIView, colors: [CGColor]) {
self.frame = CGRect(origin: CGPoint.zero, size: view.bounds.size)
self.colors = colors
let shape = CAShapeLayer()
shape.lineWidth = 14
shape.path = UIBezierPath(roundedRect: view.bounds, cornerRadius: 12).cgPath
shape.strokeColor = UIColor.black.cgColor
shape.fillColor = UIColor.clear.cgColor
self.mask = shape
}
}
TableViewCell Class – Insert CAGradientLayer
override func awakeFromNib() {
super.awakeFromNib()
reportCard.layer.insertSublayer(gradientLayer, at: 0)
...
}
TableViewCell Class – Resize the Border and Apply User Selected Design
override func layoutIfNeeded() {
super.layoutIfNeeded()
switch currentReport?.frameId {
case "sj_0099_nc_frame_001":
gradientLayer.createBorder(view: reportCard, colors: [App.BorderColors.lavender, App.BorderColors.white])
case "sj_0099_nc_frame_002":
gradientLayer.createBorder(view: reportCard, colors: [App.BorderColors.red, App.BorderColors.white])
case "sj_0099_nc_frame_003":
gradientLayer.createBorder(view: reportCard, colors: [App.BorderColors.yellow, App.BorderColors.white])
default:
gradientLayer.createBorder(view: reportCard, colors: [App.BorderColors.white, App.BorderColors.white])
}
}
2
Answers
Turns out I was looking in the wrong place all along. The code in my original post is functional, and updating the gradientLayer frame in layoutIfNeeded() or setNeedsLayout() rather than layoutSubviews() accurately draws the gradientLayer. Per Apple documentation, layoutSubviews() should not be called directly.
The source of the bug was not in my custom cell, but in my tableViewController. I had an extraneous call to reloadData().
Instead of inside awakeFromNib() use this