将角半径应用于 Storyboard 内的特定 UIView 角不适用于所有角 [英] Applying corner radius to a specific UIView corner inside Storyboard does not work for all corners
问题描述
我为此创建了一个自定义类.但它仅适用于左上角,而不适用于其他:
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屋!