UIView's border color in Interface builder doesn't work?

64,757

Solution 1

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.

Solution 2

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:

enter image description here

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:

enter image description here

Once you choose sth it is automatically added to your runtime attributes:

Solution 3

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:

Attributes inspector with the new IBInspectable options

No need to mess around with user defined runtime attributes anymore. And your changes will also show up on the canvas!

Solution 4

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

Solution 5

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
        }
    }
}
Share:
64,757
0xSina
Author by

0xSina

umadbr0

Updated on February 01, 2022

Comments

  • 0xSina
    0xSina over 2 years

    I am trying to set up a view's layer properties via IB. Everything works except for color of the border (property layer.borderColor):

    enter image description here

    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.borderColorproperty 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.

    • Admin
      Admin about 11 years
      "write extra line of code just because of this, seems like an overkill" - your app must be containing at most 10 lines of code then (with the C standard headers included) :P
    • 0xSina
      0xSina about 11 years
      @H2CO3 lol...i just don't like writing code that i don't have to
    • chuthan20
      chuthan20 about 11 years
      I've made this mistake in the past.. I hope you are aware of it.. otherwise check the first answer from this: stackoverflow.com/questions/3980251/…
    • Peter DeWeese
      Peter DeWeese almost 11 years
      You can do this with a proxy property! See my answer in stackoverflow.com/questions/12301256/…
  • 0xSina
    0xSina about 11 years
    Hmmmm...not @ my desktop so will experiment with that, but if I set masksToBounds to NO, then I can't have UIImageView rounded/corner radius :(
  • storoj
    storoj over 10 years
    i think there is no need to use associated objects because something may happen and your borderColor will be out of sync with layer.borderColor. just return [UIColor colorWithCGColor:self.layer.borderColor]
  • bainfu
    bainfu over 10 years
    You're absolutely right @storoj. It's a pattern we use for adding properties and being able to store them using categories. It's misapplied here.
  • Lloyd Sargent
    Lloyd Sargent almost 9 years
    This is the easiest solution and one that I implemented. Downside is you can forget it's there (it's only a few lines!).
  • raven_raven
    raven_raven almost 8 years
    Why borderUIColor property is assign instead of strong? It's an object type, doesn't this lead to a problem?
  • Haligen
    Haligen almost 8 years
    This saves a major headache when you're applying these to several items. Slick little tidbit.
  • etayluz
    etayluz over 7 years
    I'm sorry but this doesn't work. I have just tried it myself. @IBDesignable doesn't work on extensions of UIView. stackoverflow.com/questions/29906855/…
  • Michael Shang
    Michael Shang over 7 years
    Just tried with some tiny upper/lower case problem with Swift 3.0. Shame that I did not know this before.
  • atastrophic
    atastrophic about 7 years
    ^ because it doesn't hold the value, it passes it on.
  • jungledev
    jungledev almost 7 years
    Your answer has nothing to do with the question. The poster said he knows how to do it programmatically. Also, you can see that he already set the borderWidth to be >1 in his screenshot. He asked about doing this specifically in IB.
  • Harrison Smith
    Harrison Smith over 5 years
    As of Xcode 9 & Swift 3, this actually does work at runtime. It just doesn't display the changes in the interface builder.
  • Sinto
    Sinto over 5 years
    Please provide some explanation to your code & w help to fix the issue
  • KarenAnne
    KarenAnne over 5 years
    Hello @Eduardo, then how will I use this in Storyboard?
  • haik.ampardjian
    haik.ampardjian over 2 years
    This is actually the working solution!
  • Rafael Nobre
    Rafael Nobre over 2 years
    Adding an extension like this is unreliable at best. Definitely not the way to do it in 2021.