Reputation: 5845
The interface builder agent is crashing when I have the following code in the IBDesignable class.
self.backgroundColor = UIColor.mooButtonBackgroundColor
The representation of the color takes the form:
@nonobjc class var mooButtonBackgroundColor : UIColor {
UIColor(named: "Color.Button.Background")! // 0.129, 0.490, 0.851, 1
}
However, if I change the value returned to be:
@nonobjc class var mooButtonBackgroundColor : UIColor {
.green //UIColor(named: "Color.Button.Background")! // 0.129, 0.490, 0.851, 1
}
it all works.
I have also tried the method without the @nonobjc tag, with the same result.
Is there a restriction in IBDesigner that prevents using named colors?
Can someone suggest a method to overcome this crash in IBDesigner?
Upvotes: 0
Views: 2549
Reputation: 534893
Okay, it turns out that this was a bug. Fixed in Xcode 11.4:
Fixed a bug where using named colors in IBInspectable properties would use a fixed color at runtime, instead of the dynamic color from the asset catalog. (55248109) (FB7248211)
Upvotes: 1
Reputation: 437392
You asked:
Can someone suggest a method to overcome this crash in IBDesigner?
We can’t solve the named color problem, but we certainly can eliminate the crash by avoiding forced unwrapping operator, !
. E.g. you could use a nil
-coalescing operator, ??
, instead, so you have some fall-back color for IB, e.g.
backgroundColor = UIColor(named: "Color.Button.Background") ?? .blue
But, if you don’t force unwrap, it won’t crash. And while it won’t use your named color in IB, it will when you run the app.
Personally, I’d avoid setting any properties usually set in IB (such as backgroundColor
) in a designable view, itself. I think it’s exceedingly confusing to be looking at some view in IB, change a property, such as the background color, and not have it render correctly.
What can be configured in IB should probably be left in IB to avoid any confusion.
But let’s consider an example where I did want a custom color property to use a named color. I’d declare an @IBInspectable
color property, e.g., consider this circle view with a custom fillColor
:
@IBDesignable
class CircleView: UIView {
let shapeLayer = CAShapeLayer()
@IBInspectable var fillColor: UIColor = .blue { didSet { shapeLayer.fillColor = fillColor.cgColor } }
override init(frame: CGRect = .zero) {
super.init(frame: frame)
configure()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
configure()
}
override func layoutSubviews() {
super.layoutSubviews()
updatePaths()
}
}
private extension CircleView {
func configure() {
layer.addSublayer(shapeLayer)
shapeLayer.fillColor = fillColor.cgColor
}
func updatePaths() {
let center = CGPoint(x: bounds.midX, y: bounds.midY)
let radius = min(bounds.width, bounds.height) / 2
shapeLayer.path = UIBezierPath(arcCenter: center, radius: radius, startAngle: 0, endAngle: 2 * .pi, clockwise: true).cgPath
}
}
I’d then set the named color in IB attributes inspector:
The other approach, is to not use named colors in the asset catalog, but rather define your own UIColor
extension:
extension UIColor {
static let buttonBackground = #colorLiteral(red: 0, green: 1, blue: 1, alpha: 1)
}
Then when you want to set some color property, you can do something like:
backgroundColor = .buttonBackground
The downside of this approach is that you lose the ability to use the named color within IB for other controls. But it’s another pattern to consider.
Upvotes: 1