@IBInspectable with enum?

IosObjective CIphoneXcodeSwift

Ios Problem Overview


I'd like to create @IBInspectable element as you see at the picture below :

enter image description here

my idea is to use something like enum as type for @IBInspectable, but it looks like it's not the case, any ideas how to implement element like this ?

EDIT:

It looks like @IBInspectable supports only these types :

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

bummer

Ios Solutions


Solution 1 - Ios

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.

Solution 2 - Ios

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.

Solution 3 - Ios

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

Solution 4 - Ios

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

Solution 5 - Ios

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

Solution 6 - Ios

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.

Solution 7 - Ios

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionVlad Z.View Question on Stackoverflow
Solution 1 - Iosyusuke024View Answer on Stackoverflow
Solution 2 - IosAnthony MattoxView Answer on Stackoverflow
Solution 3 - IosMatthew CawleyView Answer on Stackoverflow
Solution 4 - IosHixFieldView Answer on Stackoverflow
Solution 5 - IosChrisView Answer on Stackoverflow
Solution 6 - IosAmin Negm-AwadView Answer on Stackoverflow
Solution 7 - IosGal MaromView Answer on Stackoverflow