I have a tableview with custom cell loaded via xib and in that cell I have status button which bottom right corner should be rounded. The button has constraints Trailing/Leading/Bottom space to superview=0 and height=30.
Without rounding it is working perfectly, as soon as I round one corner for example bottom right the constraints breaks
self.btnStatus.roundCorners(corners: [.bottomRight], radius: 7.0, borderWidth: nil, borderColor: nil)
Some guys here suggesting to call layoutSubviews() but it didn't helped me.
To be more specific I've created simple project where you can have a look into whole project.
Correct Link
ButtonRoundCorner.zip
You can get more reliable results by subclassing your button and placing your "rounding" code by overriding its layoutSubviews()
function.
First, if you want to add a border, you don't want to add multiple "border sublayers" ... so change your UIView
extension to this:
extension UIView {
func roundCorners(corners: UIRectCorner, radius: CGFloat, borderWidth: CGFloat?, borderColor: UIColor?) {
let maskPath = UIBezierPath(roundedRect: self.bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
let maskLayer = CAShapeLayer()
maskLayer.frame = self.bounds
maskLayer.path = maskPath.cgPath
self.layer.mask = maskLayer
self.layer.masksToBounds = true
if (borderWidth != nil && borderColor != nil) {
// remove previously added border layer
for layer in layer.sublayers! {
if layer.name == "borderLayer" {
layer.removeFromSuperlayer()
}
}
let borderLayer = CAShapeLayer()
borderLayer.frame = self.bounds;
borderLayer.path = maskPath.cgPath;
borderLayer.lineWidth = borderWidth ?? 0;
borderLayer.strokeColor = borderColor?.cgColor;
borderLayer.fillColor = UIColor.clear.cgColor;
borderLayer.name = "borderLayer"
self.layer.addSublayer(borderLayer);
}
}
}
Next, add a UIButton
subclass:
class RoundedButton: UIButton {
var corners: UIRectCorner?
var radius = CGFloat(0.0)
var borderWidth = CGFloat(0.0)
var borderColor: UIColor?
override func layoutSubviews() {
super.layoutSubviews()
// don't apply mask if corners is not set, or if radius is Zero
guard let _corners = corners, radius > 0.0 else {
return
}
roundCorners(corners: _corners, radius: radius, borderWidth: borderWidth, borderColor: borderColor)
}
}
This gives you a couple benefits: 1) It will update its mask layer frame when the button frame changes (rotating the device, for example), and 2) you could set these values either from your custom cell class or from cellForRowAt.
Either way, change your btnStatus
class from UIButton
to RoundedButton
- both in your storyboard and the @IBOutlet
connection.
Then change your CustomTableViewCell
to this:
class CustomTableViewCell: UITableViewCell {
@IBOutlet weak var btnStatus: RoundedButton!
override func awakeFromNib() {
super.awakeFromNib()
// set up corner maskign
btnStatus.corners = .bottomRight
btnStatus.radius = 7.0
// set if desired
// btnStatus.borderWidth = 2.0
// btnStatus.borderColor = .blue
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
And finally, your cellForRowAt
function becomes:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCell(withIdentifier: "customCell", for: indexPath) as! CustomTableViewCell
return cell
}
That should do it...
I looked at your code. The problem is that your roundCorners
function is depending on your view's bounds to set your layer's properties and you are calling roundCorners
in awakeFromNib
at which point your cell has the same bounds as in the NIB file because autolayout has not been calculated yet. You need to move your roundCorners
call into layoutSubviews
, so it gets called after autolayout is done computing your bounds.
import UIKit
class CustomTableViewCell: UITableViewCell {
@IBOutlet weak var btnStatus: UIButton!
override func layoutSubviews() {
super.layoutSubviews()
self.btnStatus.roundCorners(corners: [.bottomRight], radius: 7.0, borderWidth: nil, borderColor: nil)
}
}
EDIT also add cell.setNeedsLayout() to cellforRowAt to force layoutSubviews to be called before the cell is drawn for the first time.
Maybe your button is overlapping since it doesn't have an upper constraint? Try adding a bright border and see if you can see the top part, if not try adding a constraint to the top. Also, instead of giving it a static height, try making it a proportional height to the object above it (maybe the screen size of your device is causing it to overlap)