Home > Net >  Applying CAGradientLayer on Dynamically Sized UITableViewCell
Applying CAGradientLayer on Dynamically Sized UITableViewCell

Time:01-04

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])
        }
 }

CodePudding user response:

Instead of inside awakeFromNib() use this

override func layoutSubviews() {

   super.layoutSubviews()
   
    reportCard.layer.insertSublayer(gradientLayer, at: 0)
    reportCard.clipsToBounds = true
}

CodePudding user response:

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().

  •  Tags:  
  • Related