问题
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