I have a UITableView
whose cells contain a subview on which I need to perform three things:
- change its width constraint at runtime depending on a value inside an object specific to this cell
- change its background color depending on that same value
- round the top left and bottom left corners of the view but keep the corners on the right hand side as they are (so
layer.cornerRadius
is not an option)
I use the following code inside my custom UITableViewCell
subclass to achieve the rounded corner effect on one side of the view only, which I call from tableView:cellForRowAt:
:
func roundLeadingEdgesOfBar() {
let roundedLayer = CAShapeLayer()
roundedLayer.bounds = viewInQuestion.frame
roundedLayer.position = viewInQuestion.center
roundedLayer.path = UIBezierPath(roundedRect: viewInQuestion.bounds,
byRoundingCorners: [.topLeft, .bottomLeft],
cornerRadii: CGSize(width: 2, height: 2)).cgPath
viewInQuestion.layer.mask = roundedLayer
print("frame: \(viewInQuestion.frame)")
}
However what I see when I run this code is an effect like this:
The print
statement in the code above produces the following output, indicating that viewInQuestion
has the same frame every time when clearly on the screen it hasn't:
frame: (175.0, 139.5, 200.0, 5.0)
frame: (175.0, 139.5, 200.0, 5.0)
frame: (175.0, 139.5, 200.0, 5.0)
frame: (175.0, 139.5, 200.0, 5.0)
So I assume the width constraint on the view has not been rendered by the time I call this function. When I scroll the entire table view up until all cells are out of view, and then scroll them back into view, everything looks correct and the printed frames are all different, like I would expect:
frame: (136.5, 79.5, 238.5, 5.0)
frame: (169.5, 79.5, 205.5, 5.0)
frame: (226.0, 79.5, 149.0, 5.0)
frame: (247.5, 79.5, 127.5, 5.0)
I've read several times on SO to execute code that is dependent on constraints having been applied from within layoutSubviews
, but that gave me the same result. I even tried calling roundLeadingEdgesOfBar
from within tableView:willDisplay:forRowAt:
, to no avail.
I also found this response to a similar problem which suggests putting the mask layer code inside drawRect:
. This actually fixes 99% of the problem for me (leaving performance issues aside), but there are still corner cases (no pun intended) left for very long table views where I still see the wrong behavior.
My last resort was to call my rounding function via performSelector
with a 0.00001 delay, which works in the sense that you see the bug for about a second on screen before it then disappears - still far from ideal behavior, let alone the awful code I had to write for it.
Is there any way to reliably apply the shape layer on the view inside a UITableViewCell
using its correct runtime frame?