Search code examples
iosswiftuistackviewibdesignable

IBDesignable not working when added to UIStackView


I have this fairly simple IBDesignable RoundedLabel. It renders fine in Interface Builder and when run in simulator. If I add the view into a UIStackView, even though it renders fine in IB, corner radius is only applied to top left corner.

import UIKit

@IBDesignable class RoundedLabel: UILabel {
    @IBInspectable var cornerRadius : CGFloat = 46 {
        didSet {
            self.drawTheLabel()
        }
    }

    @IBInspectable var roundRightCorners : Bool = false {
        didSet {
            self.drawTheLabel()
        }
    }

    @IBInspectable var roundLeftCorners : Bool = false {
        didSet {
            self.drawTheLabel()
        }
    }
    @IBInspectable var borderRadius : CGFloat = 3 {
        didSet {
            self.layer.borderWidth = borderRadius
        }
    }
    @IBInspectable var borderColor : UIColor = .black {
        didSet {
            self.layer.borderColor = borderColor.cgColor
        }
    }

    private func drawTheLabel() {
        var tCorners : UIRectCorner = []
        if roundRightCorners {
            tCorners.insert(.topRight)
            tCorners.insert(.bottomRight)
        }
        if roundLeftCorners {
            tCorners.insert(.bottomLeft)
            tCorners.insert(.topLeft)
        }

        let path = UIBezierPath(roundedRect:self.bounds,
                                byRoundingCorners:tCorners,
                                cornerRadii: CGSize(width: cornerRadius, height:  cornerRadius))

        let maskLayer = CAShapeLayer()

        maskLayer.path = path.cgPath
        self.layer.mask = maskLayer
        self.layer.masksToBounds = true
    }

}

The view when not in uistackview

enter image description here

The view when added to a uistackview

enter image description here


Solution

  • The problem is this line:

    let path = UIBezierPath(roundedRect:self.bounds,
    

    That line makes no sense before self actually has its correct bounds. But at the time you are calling drawTheLabel, it doesn't yet have its correct bounds. (It is probably 1000x1000 or some other meaningless size; you can easily log/debug and find out.) You are calling drawTheLabel too soon, before the stack view has performed layout on its arranged subviews.

    Therefore the mask you are adding is way too big for the view in its final size, and you see only the top left corner of the mask; the other corners of the mask are all way off to the right and bottom and have no effect on the view because they don't overlap it.