20

For those that don't know what I'm talking about, Xcode 6.0 added new features, IBDesignable and IBInspectable.

When you tag your custom views with IBInspectable properties, those properties show up in the Attributes Inspector in IB.

Likewise, when you tag a custom UIView subclass with IBDesignable, Xcode compiles your views and invokes the code to render your view objects right in the Xcode window so you can see what they look like.

The technique for adding IBDesignable and IBInspectable attributes to custom views is pretty much identical in Swift and Objective-C. IBInspectable properties appear in the Interface Builder Attributes Inspector regardless of which language you use to define them.

I've created a category of UIView in Objective-C and an extension of UIView in Swift that promote the borderWidth, cornerRadius, borderColor, and layerBackgroundColor properties of the view's underlying layer as properties of the view. If you change the property, the extension/category does type conversion as required and forwards the change to the layer.

The IBInspectable part works great. I see and can set the new properties in the IB attributes inspector.

I could have sworn that last week, the IBDesignable attribute on my view category/extension was working too, and I could see my custom UIView category rendering in IB with it's changed layer attributes. This week it isn't working.

Was I hallucinating?

Can categories/extensions of existing system classes draw their custom UI in Interface Builder when they are set up with IBDesignable?

nhgrif
  • 61,578
  • 25
  • 134
  • 173
Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • Yes, you can... I believe. What part isn't working? You can't set the properties in the property inspector? Or you can set them in the inspector, but the view doesn't update to reflect the changes? – nhgrif Apr 27 '15 at 22:20
  • Hmm, I take that back. It looks like I'm able to get the properties to show up in property inspector, but they don't update on interface builder. However, if I compile and run, the properties *are* applied. – nhgrif Apr 27 '15 at 22:35
  • @nhgrif, I should have been clearer. That's correct. The properties show up in the inspector, I can set their values, and they then work in the running program. However the view does not reflect the changes in the IB window. – Duncan C Apr 27 '15 at 22:53
  • IBDesignable does work with custom subclasses. It's looking like it doesn't work with categories/extensions, even though I could swear it was working last week. I wonder if the latest update to Xcode broke it? – Duncan C Apr 27 '15 at 22:54
  • @nhgrif, your edit to my question was incorrect, and altered my intended meaning.. I meant it as written. My question is "can you", not "how do you". I know how. My question is, does it work. I rolled it back. – Duncan C Apr 27 '15 at 22:55
  • 1
    "Is it possible" isn't a good fit for Stack Overflow. How can you answer a "is it possible" question with anything but a "yes" or "no"? Meanwhile a "How do I" question *answers* your "is it possible" as well as encourages answers which actually demonstrate how its done (which is really what you want as well). See [this](http://meta.stackoverflow.com/questions/291599/how-to-review-is-this-possible-questions) and [this](http://meta.programmers.stackexchange.com/questions/7273/why-is-is-it-possible-to-a-poorly-worded-question/7274#7274). – nhgrif Apr 27 '15 at 23:10
  • I'm not going to edit that again (I did refix the tags though). I'm just going to let you edit this Yes-or-No question into something that would actually encourage good Stack Exchange answers. – nhgrif Apr 27 '15 at 23:17

5 Answers5

26

Since posting this question I've learned that @IBDesignable does not work for class extensions. You can add the tag, but it has no effect.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
  • 1
    Works: pod 'UIView-IBDesignable' – Avishay Cohen Jul 27 '16 at 09:19
  • 1
    No it don`t. I`ve tested it just now. Left view is a subclass of UIView, right - pure UIView. They has same atributes. http://imgur.com/a/o5Dwn Still looking for decision – Dren Aug 19 '16 at 10:38
  • 1
    Worked for me like a charm. However, I noticed that this UIView extension library does NOT work on a standalone UIView - it only applies to subclasses of UIView. Not ideal. @Dren could you try again on a subclass of UIView and confirm if it works or not ? – etayluz Aug 20 '16 at 15:56
  • @avishic, do NOT edit someone's answer to change the sense of the answer. If you disagree with the answer then post a comment to that effect, or another answer with the alternative viewpoint. IBDesignable works fine for subclasses, but not for extensions. If you have specific information to the contrary, post your own answer with a concrete example. – Duncan C Aug 20 '16 at 22:52
  • @DuncanC, Sorry, no problem. – Avishay Cohen Aug 21 '16 at 09:07
  • @DuncanC is right, it is IB_Inspectible that does work on extensions, not IB_Designable, sorry for misleading – Avishay Cohen Aug 21 '16 at 09:13
  • @etayluz , i did it before post my reply. Check image I have attached there, it is exactly situation your ask for. – Dren Aug 22 '16 at 12:52
  • It works at least in Swift3 with xCode 8. I've just implemented UIView extension to add corners and borders. Works for any UIView subclass (basically all UI elements). The problem is that IB agent might crash from time to time because it refreshes all elements and uses a lot of power – Paulius Vindzigelskis Jun 22 '17 at 17:03
9

I was able to make it work with code below, but the side effect is that some times IB agent in storyboard crashes because it has to refresh too many UI elements. Restarting Xcode fixes problem temporarily until next crash. Maybe that's the problem OP is facing

@IBDesignable
extension UIView
{

    @IBInspectable
    public var cornerRadius: CGFloat
    {
        set (radius) {
            self.layer.cornerRadius = radius
            self.layer.masksToBounds = radius > 0
        }

        get {
            return self.layer.cornerRadius
        }
    }

    @IBInspectable
    public var borderWidth: CGFloat
    {
        set (borderWidth) {
            self.layer.borderWidth = borderWidth
        }

        get {
            return self.layer.borderWidth
        }
    }

    @IBInspectable
    public var borderColor:UIColor?
    {
        set (color) {
            self.layer.borderColor = color?.cgColor
        }

        get {
            if let color = self.layer.borderColor
            {
                return UIColor(cgColor: color)
            } else {
                return nil
            }
        }
    }
}

That's why I am trying to add where clause to reduce subclasses which should extend this functionality: Generic IBDesginables UIView extension

Savas Adar
  • 4,083
  • 3
  • 46
  • 54
Paulius Vindzigelskis
  • 2,121
  • 6
  • 29
  • 41
  • I'm upvoting this because it worked fine for an NSView in my Mac app. I have not seen a crash using this code. I used this technique for a different value, not for borders, but it should not matter what variables are set this way. – David Rector May 17 '23 at 00:38
1

@IBDesignable work with UIView extension only in custom class.For example. UILabel is a default sub-class of UIView. It won't work there, but if you make a custom class called MyUILabel subclassing UILabel. assign the MyUILabel class to the Label your are working on. Then your corner radius in UIView extension will work of this MyUILabel. ( I guess the first week it work for you is because you are dealing with some custom class.)

Lawrence Cheuk
  • 434
  • 1
  • 4
  • 13
1

I've made this work for my use case by having one @IBDesignable UIView that I set as the top view in my view controller. My particular use case is making ClassyKit styling visible in Interface Builder on the default UIKit views without have to subclass just for that and it's working great.

Here's an example of how you could set it up:

// in Interface Builder set the class of your top view controller view to this
@IBDesignable class DesignableView: UIView {
}

extension UIView {
    open override func prepareForInterfaceBuilder() {
        subviews.forEach {
            $0.prepareForInterfaceBuilder()
        }
    }
}

extension UILabel {
// just an example of doing something
    open override func prepareForInterfaceBuilder() {
        layer.cornerRadius = 8
        layer.masksToBounds = true
        backgroundColor = .red
        textColor = .green
    }
}
  • Sigh, it looks like IB doesn't set the runtime attributes on the non-designable classes which is probably going to make this a non starter for my use. Everything appeared to be working in my use case initially but I was only testing a style sheet with a selector on the view class and not a specific style--which depends on having a runtime attribute. – Richard Burgess Oct 29 '16 at 19:35
-4

This code block is working well for me.

import UIKit

public extension UIView {
    @IBInspectable public var cornerRadius: CGFloat {
        get {
            return layer.cornerRadius
        }
        set {
            layer.cornerRadius = newValue
            layer.masksToBounds = newValue > 0
        }
    }
}

NOTE It might not work when being imported from a framework. I am trying to find out the reason now.

Ducky
  • 2,754
  • 16
  • 25