使用枚举进行检查?

我想创建 @IBInspectable元素,如下图所示:

enter image description here

我的想法是使用类似 enum 的东西作为 @IBInspectable的类型,但看起来并不是这样,有什么想法如何实现这样的元素吗?

编辑:

看起来 @IBInspectable只支持这些类型:

  • Int
  • CGFloat
  • Double
  • String
  • Bool
  • CGPoint
  • CGSize
  • CGRect
  • UIColor
  • UIImage

真扫兴

27823 次浏览

That's not possible (for now). You can only use those types that you see in User Defined Runtime Attributes section.

From Apple's doc:

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.

As @sikhapol answered, this is not possible. The workaround I use for this is to have a bunch of IBInspectable bools in my class and just select one in interface builder. For added security that multiple ones are not set, add an NSAssert in the setter for each one.

- (void)setSomeBool:(BOOL)flag
{
if (flag)
{
NSAssert(!_someOtherFlag && !_someThirdFlag, @"Only one flag can be set");
}
}

This is a little tedious and a bit sloppy IMO, but it's the only way to accomplish this kind of behavior that I can think of

I want to add that the identifiers of an enum are not available at runtime for anybody in Objective-C. So there cannot be a possibility to display it anywhere.

I do this using a Inspectable NSInteger value and override the setter to allow it to set the enum. This has the limitation of not using a popup list and if you change your enum values, then the interface options will not update to match.

Example.

In Header File:

typedef NS_ENUM(NSInteger, LabelStyle)
{
LabelStyleContent = 0, //Default to content label
LabelStyleHeader,
};


...


@property LabelStyle labelStyle;
@property (nonatomic, setter=setLabelAsInt:) IBInspectable NSInteger labelStyleLink;

In the implementation file:

- (void)setLabelAsInt:(NSInteger)value
{
self.labelStyle = (LabelStyle)value;
}

You could optionally add some logic in there to ensure that it is being set to a valid value

Another work-around for this is to alter how an enumeration property appears to interface builder. For example:

#if TARGET_INTERFACE_BUILDER
@property (nonatomic, assign) IBInspectable NSInteger fontWeight;
#else
@property (nonatomic, assign) FontWeight fontWeight;
#endif

This assumes an enum called FontWeight. It relies on the fact that enums and their raw integer values can be used somewhat interchangeably in Objective-C. After doing this you are able to specify an integer in Interface builder for the property which is not ideal, but works, and retains a small amount of type safety when using the same property programatically.

This is a better alternative than declaring a separate integer property because you don't need to write extra logic to handle a second integer property which could also be used to accomplish the same thing.

However, this does not work with Swift because we're not able to implicitly cast from an integer to an enum. Any thoughts on solving that would be appreciated.

My solution was to do :

@IBInspectable
var keyboardType = UIKeyboardType.default.rawValue {
didSet {
textField.keyboardType = UIKeyboardType(rawValue: keyboardType)!
}
}

On the IB itself, you will need to set an int in the keyboardType field

Sikhapol is correct, enums are not yet supported also not in xCode 9. I believe the safest approach is to use enums as strings and implement a "shadow" (private) IBInspectable var. Here is an example of a BarBtnPaintCode item which represents a barbutton item that can be styled with a custom icon (that is done using PaintCode) right inside Interface Builder (swift 4).

In interface build you just enter the string (identical to the enum value), which keeps it clear (if you are entering numbers nobody knows what they mean)

class BarBtnPaintCode: BarBtnPaintCodeBase {


enum TypeOfButton: String {
case cancel
case ok
case done
case edit
case scanQr
//values used for tracking if wrong input is used
case uninitializedLoadedFromStoryboard
case unknown
}


var typeOfButton = TypeOfButton.uninitializedLoadedFromStoryboard


@IBInspectable private var type : String {
set {
typeOfButton = TypeOfButton(rawValue: newValue) ?? .unknown
setup()
}
get {
return typeOfButton.rawValue
}
}


required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}


init(typeOfButton: TypeOfButton, title: String? = nil, target: AnyObject?, action: Selector) {
super.init()
self.typeOfButton = typeOfButton
setup()
self.target = target
self.action = action
self.title  = title
}


override func setup() {
//same for all
setTitleTextAttributes([NSAttributedStringKey.font : UIFont.defaultFont(size: 15)],for: UIControlState.normal)
//depending on the type
switch typeOfButton {
case .cancel  :
title = nil
image = PaintCode.imageOfBarbtn_cancel(language: currentVisibleLanguage)
case .ok      :
title = nil
image = PaintCode.imageOfBarbtn_ok(language: currentVisibleLanguage)
case .done    :
title = nil
image = PaintCode.imageOfBarbtn_done(language: currentVisibleLanguage)
case .edit    :
title = nil
image = PaintCode.imageOfBarbtn_edit(language: currentVisibleLanguage)
case .uninitializedLoadedFromStoryboard :
title = nil
image = PaintCode.imageOfBarbtn_unknown
break
case .unknown:
log.error("BarBtnPaintCode used with unrecognized type")
title = nil
image = PaintCode.imageOfBarbtn_unknown
break
}


}


}