How to create an IBInspectable of type enum

前端 未结 6 1424
遇见更好的自我
遇见更好的自我 2020-12-04 11:56

enum is not an Interface Builder defined runtime attribute. The following does not show in Interface Builder\'s Attributes Inspector:



        
相关标签:
6条回答
  • 2020-12-04 12:24

    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
    
    0 讨论(0)
  • 2020-12-04 12:26

    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.

    0 讨论(0)
  • 2020-12-04 12:28

    Swift 3 solution based on SwiftArchitect

    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
    
    0 讨论(0)
  • 2020-12-04 12:32

    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
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-04 12:33

    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
    
    0 讨论(0)
  • 2020-12-04 12:39

    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.

    0 讨论(0)
提交回复
热议问题