UIView's border color in Interface builder doesn't work?
I am trying to set up a view's layer properties via IB. Everything works except for color of the border (property layer.borderColor
):
I remember running into this problem a year ago and I ended up doing it programatically. And still, I can do this programmatically, but I am curious why the layer.borderColor
property never works via interface builder. I don't want to import QuartzCore
, and then write extra line of code just because of this, seems like an overkill.
It's possible to do this, but it's not a built-in feature. This is because the Color
type in the User Defined Runtime Attributes panel creates a UIColor
, but layer.borderColor
holds a CGColorRef
type. Unfortunately, there's no way to assign a CGColorRef
type in Interface Builder.
However, this is possible through a proxy property. See Peter DeWeese's answer to a different question for a possible solution to this problem. His answer defines a category that allows a proxy color to be set through Interface Builder.
You have to create Category for CALayer:
CALayer+UIColor.h
#import <QuartzCore/QuartzCore.h>
#import <UIKit/UIKit.h>
@interface CALayer(UIColor)
// This assigns a CGColor to borderColor.
@property(nonatomic, assign) UIColor* borderUIColor;
@end
CALayer+UIColor.m
#import "CALayer+UIColor.h"
@implementation CALayer(UIColor)
- (void)setBorderUIColor:(UIColor*)color {
self.borderColor = color.CGColor;
}
- (UIColor*)borderUIColor {
return [UIColor colorWithCGColor:self.borderColor];
}
@end
And then in User Defined Runtime attributes You can use it as it is on image below:
For Swift it is much more simple:
import QuartzCore
extension CALayer {
@IBInspectable var borderUIColor: UIColor? {
get {
guard let borderColor = borderColor else { return nil }
return UIColor(cgColor: borderColor)
}
set {
borderColor = newValue?.cgColor
}
}
}
Then in Xcode you can use it like this:
Once you choose sth it is automatically added to your runtime attributes:
Copy and paste this class:
import UIKit
@IBDesignable class BorderView : UIView {
@IBInspectable var borderColor: UIColor = .clear {
didSet {
layer.borderColor = borderColor.cgColor
}
}
@IBInspectable var borderWidth: CGFloat = 0 {
didSet {
layer.borderWidth = borderWidth
}
}
@IBInspectable var cornerRadius: CGFloat = 0 {
didSet {
layer.cornerRadius = cornerRadius
}
}
}
Now in Interface Builder, go to the Identity inspector and set your view as a CustomView class.
After that, check out your Attributes Inspector:
No need to mess around with user defined runtime attributes anymore. And your changes will also show up on the canvas!
My two cents for porting Bartłomiej Semańczyk's answer to Swift:
Create an extension for CALayer in your view controller:
import UIKit
extension CALayer {
func borderUIColor() -> UIColor? {
return borderColor != nil ? UIColor(CGColor: borderColor!) : nil
}
func setBorderUIColor(color: UIColor) {
borderColor = color.CGColor
}
}
Use IBDesignable instead of Runtime Attributes it is more clear.
Put this code in any class and edit the properties direct on the storyboard.
import UIKit
@IBDesignable extension UIView {
@IBInspectable var borderColor:UIColor? {
set {
layer.borderColor = newValue!.CGColor
}
get {
if let color = layer.borderColor {
return UIColor(CGColor:color)
}
else {
return nil
}
}
}
@IBInspectable var borderWidth:CGFloat {
set {
layer.borderWidth = newValue
}
get {
return layer.borderWidth
}
}
@IBInspectable var cornerRadius:CGFloat {
set {
layer.cornerRadius = newValue
clipsToBounds = newValue > 0
}
get {
return layer.cornerRadius
}
}
}