如何创建一个类型为枚举的IBInspectable

90
枚举(enum)不是Interface Builder定义的运行时属性。以下内容不会显示在Interface Builder的属性检查器中:
enum StatusShape:Int {
    case Rectangle = 0
    case Triangle = 1
    case Circle = 2
}
@IBInspectable var shape:StatusShape = .Rectangle

根据文档:

您可以将IBInspectable属性附加到类声明、类扩展或类别中任何受Interface Builder定义的运行时属性支持的类型的属性上:布尔值、整数或浮点数、字符串、本地化字符串、矩形、点、大小、颜色、范围和nil。

问:我如何在Interface Builder的属性检查器中查看一个枚举


1
那个列表里面有枚举吗?你为什么认为你可以使用枚举? - Paulw11
13
我可以翻译为:我想直接从IB选择一个“enum” case 或者像UIKit原生对象一样选择一个UIFont会很方便。请注意,翻译保持了原文的意思和语气,同时也更加通俗易懂。 - SwiftArchitect
6个回答

83

Swift 3

@IBInspectable var shape: StatusShape = .Rectangle只是在Interface Builder中创建了一个空白条目:

不可用于IB

使用适配器,作为Swift和Interface Builder之间的桥梁
shapeAdapter可以从IB进行检查:

   // IB: use the adapter
   @IBInspectable var shapeAdapter:Int {
        get {
            return self.shape.rawValue
        }
        set( shapeIndex) {
            self.shape = StatusShape(rawValue: shapeIndex) ?? .Rectangle
        }
    }

IB可用

与条件编译方法(使用 #if TARGET_INTERFACE_BUILDER)不同,shape 变量的类型不会随着目标的变化而改变,可能需要进一步修改源代码以应对 shape:NSIntegershape:StatusShape 变化:

   // Programmatically: use the enum
   var shape:StatusShape = .Rectangle

完整代码

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

► 在GitHub上找到此解决方案。


你为什么要将 shapeAsInt 作为存储属性而不是计算属性呢? - nhgrif
没错。你不能将属性设置为writeonly,但是计算属性不会像存储属性一样创建一个后备实例变量。 - nhgrif
添加了@nhgrif提出的无后备存储解决方案。 - SwiftArchitect
1
抱歉我给你的回答点了个踩。我原本以为使用TARGET_INTERFACE_BUILDER可以解决问题,但是我被误导了。很抱歉,在这里祝你好运。 - Dan Rosenstark
快进到2017年,这个问题仍然存在吗? - NoSixties
显示剩余2条评论

44

你可以使用字符串来设置可检查的枚举,而不是使用整数。虽然这种方式没有下拉菜单方便,但至少提供了一定程度的可读性。

仅适用于Swift:

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

通过为枚举类型添加初始化程序,也可以使其与objective-c兼容。但是,在Swift代码中,编译器只会针对IB-only属性显示“不可用”错误。

具有Obj-C兼容性的Swift选项:

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

2
我喜欢仅限Swift的版本:它允许在IB中使用简单明了的英语。 - SwiftArchitect
有人知道如何使用这种方法在IB中获取以字符串形式呈现的下拉选项吗? - airowe
我想知道是否可能使用下拉菜单来完成这个任务。 - Wael

19

我记不得Swift的语法了,但是这是我在Obj-C中解决问题的方法。

#if TARGET_INTERFACE_BUILDER
@property (nonatomic) IBInspectable NSInteger shape;
#else
@property (nonatomic) StatusShape shape;
#endif

1
我有条件汇编的唯一位置就在声明中的一个地方。在 obj-c 中,枚举是整数值,因此没有其他地方需要条件汇编。假设苹果公司会修复这个问题,那么只需要删除一个地方的条件汇编。 - Jason Bobier
这是一个机智而棒的解决方案,并在此提到http://nshipster.com/ibinspectable-ibdesignable/。 - Dan Rosenstark

7

2020年更新 - @SwiftArchitect的回答已经更新:

以下是一个包含所有今天语法的典型示例:

在这里输入图片描述

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
    }

对于这个关键的Swift/iOS QA,

• @SwiftArchitect的非常古老的回答是完全正确的,但是

• 我刚刚更新了它,并添加了关键的“不可用”事项,这在Swift中现在是可能的。


4
这是一个有用的旧帖子。我已经针对Swift 4.0和Xcode 9.0调整了我的答案 - Swift 4在这个问题上有自己的小问题。我有一个带有枚举类型的@IBInspectable变量,而Xcode 9.0不高兴,显示这个“属性不能被标记为@IBInspectable,因为它的类型不能在Objective-C中表示”。
@Eporediese在部分解决了这个问题(适用于Swift3); 在故事板中使用属性,但在其余代码中使用简单枚举。下面是一个更完整的代码集,可以使你在两种情况下都能使用属性。
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

2
Swift 3基于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

2
如果您将此IB-inspectable属性包装在#if TARGET_INTERFACE_BUILDER块中,它只会影响Interface Builder中的渲染,而不是运行时。这可能会导致意外的行为,并且您始终会在控制台中收到警告:Failed to set (statusShapeIB) user defined inspected property ...: this class is not key value coding-compliant for the key statusShapeIB. - Mischa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接