I have a UITableView
whose cells contain a subview on which I need to perform three things:
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?
I think what you should be doing is that,
cellForRowAtIndexPath
, try setting constraints in the setter of that particular value. setNeedsLayout
which will make a future call to layoutIfNeeded
-> layoutSubviews
layoutSubviews
, set the rounded corners. I hope this will help you.
Instead of calling a function to "round the edges," I suggest creating a UIView
subclass and let it handle the rounding.
For example:
class BulletBar: UIView {
override func layoutSubviews() {
let roundedLayer = CAShapeLayer()
roundedLayer.frame = bounds
roundedLayer.path = UIBezierPath(roundedRect: bounds,
byRoundingCorners: [.topLeft, .bottomLeft],
cornerRadii: CGSize(width: 2, height: 2)).cgPath
layer.mask = roundedLayer
}
}
Now, set the class of your "bar" subview in the cell to BulletBar. Use constraints to pin it to the right and bottom and to constrain the height and width. Create an IBOutlet
for the width constraint, and then set the barWidthConstraint.constant
as desired.
The class itself will handle rounding the corners.
Result:
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.