How to create an IBInspectable of type enum
IosXcodeSwiftInterface BuilderIos Problem Overview
enum
is not an Interface Builder defined runtime attribute.
The following does not show in Interface Builder's Attributes Inspector:
enum StatusShape:Int {
case Rectangle = 0
case Triangle = 1
case Circle = 2
}
@IBInspectable var shape:StatusShape = .Rectangle
From the documentation: You can attach the IBInspectable attribute to any property in a class declaration, class extension, or category for any type that’s supported by the Interface Builder defined runtime attributes: boolean, integer or floating point number, string, localized string, rectangle, point, size, color, range, and nil.
Q: How can I see an enum
in Interface Builder's Attributes Inspector?
Ios Solutions
Solution 1 - Ios
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
@IBDesignable
class 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.
Solution 2 - Ios
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 enum
enum 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 code
var 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
}
}
}
Solution 3 - Ios
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
Solution 4 - Ios
For 2020 - @SwiftArchitect answer updated for today:
Here's a typical full example with all of today's syntax
import UIKit
@IBDesignable class ClipLabels: UILabel {
enum Side: Int { case left, right }
var side: Side = .left {
didSet {
common()
}
}
@available(*, unavailable, message: "IB only")
@IBInspectable var leftRight01: Int {
get {
return self.side.rawValue
}
set(index) {
self.side = Side(rawValue: index) ?? .left
}
}
and just an example of use ...
switch side {
case .left:
textColor = .red
case .right:
textColor = .green
}
For this critical Swift/iOS QA,
• the very old answer of @SwiftArchitect is perfectly correct but
• I've just updated it and added the critical "unavailable" thing, which is now possible in Swift.
Solution 5 - Ios
This is an old thread but useful. I have adapted my answer to swift 4.0 and Xcode 9.0 - Swift 4 has its own little issues with this problem. I am having an @IBInspectable variable with enum type and Xcode 9.0 is not happy, showing me this "Property cannot be marked @IBInspectable because its type cannot be representing in Objective-c"
@Eporediese answers this problem (for swift3) in part; using a property for the storyboard but a straight enum for the rest of the code. Below is a more complete code set that gives you a property to work with in both cases.
enum StatusShape: Int {
case Rectangle = 0
case Triangle = 1
case Circle = 2
}
var _shape:StatusShape = .Rectangle // this is the backing variable
#if TARGET_INTERFACE_BUILDER
@IBInspectable var shape: Int { // using backing variable as a raw int
get { return _shape.rawValue }
set {
if _shape.rawValue != newValue {
_shape.rawValue = newValue
}
}
}
#else
var shape: StatusShape { // using backing variable as a typed enum
get { return _shape }
set {
if _shape != newValue {
_shape = newValue
}
}
}
#endif
Solution 6 - Ios
Swift 3 solution based on [SwiftArchitect][1]
enum StatusShape: Int {
case rectangle, triangle, circle
}
var statusShape: StatusShape = .rectangle
#if TARGET_INTERFACE_BUILDER
@IBInspectable var statusShapeIB: Int {
get {
return statusShape.rawValue
}
set {
guard let statusShape = StatusShape(rawValue: newValue) else { return }
self.statusShape = statusShape
}
} //convenience var, enum not inspectable
#endif
[1]: https://stackoverflow.com/a/27435318/1415703 "SwiftArchitect"