将角半径应用于 Storyboard 内的特定 UIView 角不适用于所有角 [英] Applying corner radius to a specific UIView corner inside Storyboard does not work for all corners

查看:21
本文介绍了将角半径应用于 Storyboard 内的特定 UIView 角不适用于所有角的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我为此创建了一个自定义类.但它仅适用于左上角,而不适用于其他:

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
    }
}

这是在模拟器中运行时的外观,我为所有 4 个角应用了圆角半径:

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

这是怎么回事?TopLeft 有效,TopRight 轻微,底角完全无效.我很困惑.

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

推荐答案

您应该更新 layoutSubviews 覆盖中的掩码.当约束更新视图的 frame 时,layoutSubviews 被调用,所以这是更新掩码的正确位置.

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.

顺便说一句,我不鼓励使用 awakeFromNib 来配置视图.如果您使用故事板,它可以正常工作,但以编程方式创建的视图不会调用它.如果您确实想在创建视图时对其进行配置,最好将代码放在一些由 init(frame:)init(coder:).这样它既适用于故事板,也适用于以编程方式创建的视图.

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.

因此:

@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
    }
}

<小时>

或者,如果面向 iOS 11 及更高版本,我们可以让 CoreAnimation 进行四舍五入:


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
    }
}

后一种方法的好处是,如果我们碰巧为视图的 frame 设置动画,CoreAnimation 将尊重我们的圆角:

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:

如果您使用前面提到的 layoutSubviews 方法,那么您必须手动管理动画(例如使用 CADisplayLink).

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

这篇关于将角半径应用于 Storyboard 内的特定 UIView 角不适用于所有角的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆