Whirlwind Whirlwind - 2 years ago 153
iOS Question

Custom cell is not correctly drawn when a row is inserted using insertRows(atPaths) method

I have a custom cell, which is subclass of

UITableViewCell
, call it
Cell
.

I have added a
UITableViewCell
through a storyboard to a tableview ( tableview's content is set to Dynamic Prototypes) and have set cells class to
Cell
. Also I have set the reuse identifier appropriately.

The cell is defined in
xib
, and I load it like this:

self.tableView.register(UINib(nibName: "Cell", bundle: nil), forCellReuseIdentifier: "CellIdentifier")


The cell itself has only two elements, and those are, one container view (a
UIView
) and one image view (a
UIImageView
).

Both of those elements are connected through outlets to the
Cell
.

These two elements are positioned in a
xib
using the
Autolayout
constraints. Also, before the cell is about to display, I do something like this in my
ViewController
(tried both
cellForRowAtPath
and
willDisplayCellForRowAtPath
methods):

cell.imageView.layer.cornerRadius = cell.imageView.layer.frame.height / 2.0
cell.imageView.layer.masksToBounds = true
cell.containerView.layer.cornerRadius = cell.containerView.layer.frame.height / 2.0
cell.containerView.layer.borderColor = UIColor.lightGray.cgColor
cell.containerView.layer.borderWidth = 1


and in both cases, at the time when I call
UITableView's insertRows(atIndexPaths)
method, the cell is inserted, but it is not displayed properly. The next time, i insert new row (cell), the previous one is drawn correctly...And so on...

Here is how I insert cells (on a button click event):

func addCell(_ title: String) {
titles.append(title)
let indexPath = IndexPath(row: messages.count - 1, section: 0)
tableView.beginUpdates()
tableView.insertRows(at: [indexPath], with: .bottom)
tableView.endUpdates()
tableView.scrollToRow(at: indexPath, at: .bottom, animated: true)
}


So, how to have a cell drawn correctly in this case?

Answer Source

Talked with you about this outside of StackOverflow, posting it here for others benefit.

I created a demo project that has the same issue you mentioned. I found a workaround by subclassing the cell and overriding layoutSubviews and setting the cornerRadius using DispatchQueue.main.async {}:

class TestCell: UITableViewCell {

    @IBOutlet weak var colorImageView: UIImageView!

    override func layoutSubviews() {
        super.layoutSubviews()
        DispatchQueue.main.async {
            self.colorImageView.layer.cornerRadius = min(self.colorImageView.frame.width, self.colorImageView.frame.height) / 2
        }
    }

}

Another workaround I found was by using an observer(Swift 4) and a layoutIfNeeded() call before setting the cornerRadius

class TestCell: UITableViewCell {

    @IBOutlet weak var colorImageView: UIImageView!

    var observer: NSKeyValueObservation?

    override func awakeFromNib() {
        super.awakeFromNib()
        observer = self.observe(\.frame) { (cell, change) in
            self.layoutIfNeeded()
            self.colorImageView.layer.cornerRadius = min(self.colorImageView.frame.width, self.colorImageView.frame.height) / 2
        }
    }

}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download