How to create an IBInspectable of type enum
Swift 3
@IBInspectable var shape:StatusShape = .Rectangle
merely creates a blank entry in Interface Builder:
Use an adapter, which will acts as a bridge between Swift and Interface Builder.shapeAdapter
is inspectable from IB:
// IB: use the adapter @IBInspectable var shapeAdapter:Int { get { return self.shape.rawValue } set( shapeIndex) { self.shape = StatusShape(rawValue: shapeIndex) ?? .Rectangle } }
Unlike the conditional compilation approach (using #if TARGET_INTERFACE_BUILDER
), the type of the shape
variable does not change with the target, potentially requiring further source code changes to cope with the shape:NSInteger
vs. shape:StatusShape
variations:
// Programmatically: use the enum var shape:StatusShape = .Rectangle
Complete code
@IBDesignableclass ViewController: UIViewController { enum StatusShape:Int { case Rectangle case Triangle case Circle } // Programmatically: use the enum var shape:StatusShape = .Rectangle // IB: use the adapter @IBInspectable var shapeAdapter:Int { get { return self.shape.rawValue } set( shapeIndex) { self.shape = StatusShape(rawValue: shapeIndex) ?? .Rectangle } }}
► Find this solution on GitHub.
Instead of setting your inspectable enums with ints, you could also set them with strings. Although not quite as preferable as a dropdown, at least this option offers some level of readability.
Swift-only Option:
// 1. Set up your enumenum Shape: String { case Rectangle = "rectangle" // lowercase to make it case-insensitive case Triangle = "triangle" case Circle = "circle"}// 2. Then set up a stored property, which will be for use in codevar shape = Shape.Rectangle // default shape// 3. And another stored property which will only be accessible in IB (because the "unavailable" attribute prevents its use in code)@available(*, unavailable, message: "This property is reserved for Interface Builder. Use 'shape' instead.")@IBInspectable var shapeName: String? { willSet { // Ensure user enters a valid shape while making it lowercase. // Ignore input if not valid. if let newShape = Shape(rawValue: newValue?.lowercased() ?? "") { shape = newShape } }}
It is possible to also get this to work with objective-c as well, by adding an initializer to the enum. However, the compiler will only show the "unavailable" error for your IB-only properties in swift code.
Swift Option with Obj-C Compatibility:
@objc enum Shape: Int { case None case Rectangle case Triangle case Circle init(named shapeName: String) { switch shapeName.lowercased() { case "rectangle": self = .Rectangle case "triangle": self = .Triangle case "circle": self = .Circle default: self = .None } }}var shape = Shape.Rectangle // default shape@available(*, unavailable, message: "This property is reserved for Interface Builder. Use 'shape' instead.")@IBInspectable var shapeName: String? { willSet { if let newShape = Shape(rawValue: newValue?.lowercased() ?? "") { shape = newShape } }}
I can't remember the swift syntax, but this is how I solved it in obj-c
#if TARGET_INTERFACE_BUILDER@property (nonatomic) IBInspectable NSInteger shape;#else@property (nonatomic) StatusShape shape;#endif