Applying corner radius to a specific UIView corner inside Storyboard does not work for all corners

倖福魔咒の 提交于 2020-03-02 04:26:27

问题


I made a custom class for this. But it works only for top left corner, not the others:

@IBDesignable
public class RoundedView: UIView {

    @IBInspectable public var topLeft: Bool = false
    @IBInspectable public var topRight: Bool = false
    @IBInspectable public var bottomLeft: Bool = false
    @IBInspectable public var bottomRight: Bool = false
    @IBInspectable public var cornerRadius: Int = 0

    public override func awakeFromNib() {
        var options = UIRectCorner()

        if topLeft { options =  options.union(.topLeft) }
        if topRight { options =  options.union(.topRight) }
        if bottomLeft { options =  options.union(.bottomLeft) }
        if bottomRight { options =  options.union(.bottomRight) }

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

        let maskLayer = CAShapeLayer()

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

Here is how it looks when running in simulator and I apply corner radius for all 4 corners:

What's going on here ? TopLeft works, TopRight slightly and bottom corners not at all. I am confused.


回答1:


You should update the mask in the override of layoutSubviews. As the constraints update the frame of the view, layoutSubviews is called, so that’s the right place to update the mask.


As an aside, I’d discourage the use of awakeFromNib to configure the view. It works fine if you use storyboard, but programmatically created views won’t call this. If you do want to configure the view when it’s created, it’s better to put the code in some private configuration method which is called by init(frame:) and init(coder:). That way it works for both storyboards and programmatically created views.

I might also suggest observers on the inspectable properties, to trigger the update of the corner rounding. You want the corner rounding to update automatically if you change these properties.


Thus:

@IBDesignable
public class RoundedView: UIView {

    @IBInspectable public var topLeft: Bool = false      { didSet { setNeedsLayout() } }
    @IBInspectable public var topRight: Bool = false     { didSet { setNeedsLayout() } }
    @IBInspectable public var bottomLeft: Bool = false   { didSet { setNeedsLayout() } }
    @IBInspectable public var bottomRight: Bool = false  { didSet { setNeedsLayout() } }
    @IBInspectable public var cornerRadius: CGFloat = 0  { didSet { setNeedsLayout() } }

    public override func layoutSubviews() {
        super.layoutSubviews()

        var options = UIRectCorner()

        if topLeft     { options.formUnion(.topLeft) }
        if topRight    { options.formUnion(.topRight) }
        if bottomLeft  { options.formUnion(.bottomLeft) }
        if bottomRight { options.formUnion(.bottomRight) }

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

        let maskLayer = CAShapeLayer()

        maskLayer.path = path.cgPath
        layer.mask = maskLayer
    }
}

Or, alternatively, if targeting iOS 11 and later, we can let CoreAnimation do the rounding:

@IBDesignable
public class RoundedView: UIView {

    @IBInspectable public var topLeft: Bool = false      { didSet { updateCorners() } }
    @IBInspectable public var topRight: Bool = false     { didSet { updateCorners() } }
    @IBInspectable public var bottomLeft: Bool = false   { didSet { updateCorners() } }
    @IBInspectable public var bottomRight: Bool = false  { didSet { updateCorners() } }
    @IBInspectable public var cornerRadius: CGFloat = 0  { didSet { updateCorners() } }

    public override init(frame: CGRect = .zero) {
        super.init(frame: frame)
        updateCorners()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        updateCorners()
    }
}

private extension RoundedView {
    func updateCorners() {
        var corners = CACornerMask()

        if topLeft     { corners.formUnion(.layerMinXMinYCorner) }
        if topRight    { corners.formUnion(.layerMaxXMinYCorner) }
        if bottomLeft  { corners.formUnion(.layerMinXMaxYCorner) }
        if bottomRight { corners.formUnion(.layerMaxXMaxYCorner) }

        layer.maskedCorners = corners
        layer.cornerRadius = cornerRadius
    }
}

The nice thing about this latter approach is that CoreAnimation will honor our corner rounding if we happen to be animating the frame of the view:

If you use the aforementioned layoutSubviews approach, then you have to manage the animation manually (e.g. with CADisplayLink).



来源:https://stackoverflow.com/questions/56383129/applying-corner-radius-to-a-specific-uiview-corner-inside-storyboard-does-not-wo

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!