Search code examples
swiftuitableviewcashapelayer

Laggy UITableView scrolling because of CAShapeLayer (Swift 3)


I have a UITableView within a UIView, and it is made up of custom cells defined by the following class:

class CustomAddFriendTableViewCell: UITableViewCell {
    @IBOutlet weak var UsernameLbl: UILabel! 
    @IBOutlet weak var ProfileImg: UIImageView!
    @IBOutlet weak var AddFriendBtn: UIButton!
}

Within the ViewController's tableview(_:cellForRowAt:) method I call the following function to layout the cell's ProfileImg:

private func layoutProfilePics(with cell:CustomAddFriendTableViewCell) {
    //create gradient
    let gradient = CAGradientLayer()
    gradient.frame =  CGRect(origin: CGPoint.zero, size: cell.ProfileImg.frame.size)
    gradient.colors = [Colors.blueGreen.cgColor, Colors.yellow.cgColor]

    //create gradient mask
    let shape = CAShapeLayer()
    shape.lineWidth = 3
    shape.path = UIBezierPath(ovalIn: cell.ProfileImg.bounds).cgPath // commenting this out makes lag go away
    shape.strokeColor = UIColor.black.cgColor // commenting this out makes lag go away
    shape.fillColor = UIColor.clear.cgColor
    gradient.mask = shape

    cell.ProfileImg.layoutIfNeeded()
    cell.ProfileImg.clipsToBounds = true
    cell.ProfileImg.layer.masksToBounds = true
    cell.ProfileImg.layer.cornerRadius = cell.ProfileImg.bounds.size.width/2.0
    cell.ProfileImg.layer.addSublayer(gradient)

}

This code causes ProfileImg to be a circle and have a border with a blue-green gradient. The two lines with comments next to them make the scrolling very smooth (meaning the gradient is not what is causing the lag), so I assume that rendering the CAShapeLayer (specifically the stroke) is causing the problem (hence the question title). What can I do to improve the tableview's scrolling performance?

Also, I am not sure if this is an XCode bug or if it has something to do with my problem, but in the Project Navigator's Instruments pane, when I run the app and scroll the laggy UITableView, the FPS does not reflect the lag, even though I can clearly tell that it is very laggy. In fact, there are no noticeable differences in any of the components in the pane (CPU, Memory, Energy Impact, etc.).

Update:

I tried moving the layoutProfilePics(with:) function into the CustomAddFriendTableViewCell's prepareForReuse() function and I also tried putting layoutProfilePics(with:) in its layoutSubviews() function but neither of them improved the scrolling.


Solution

  • That code is meant to be called once for each cell and for that, it shouldn't be called in tableView(_:cellForRowAt:). Try moving that code to awakeFromNib() and if you're using a nib for your custom cell, use the following code in viewDidLoad() to load the the outlets:

    let addFriendCellNib = UINib(nibName: "CustomAddFriendTableViewCell", bundle: nil)
    tableView.register(addFriendCellNib, forCellReuseIdentifier: "addFriendCell")
    

    I hope that works for you.