@IBInspectable con enumeración?

86

Me gustaría crear un @IBInspectableelemento como se ve en la imagen de abajo:

ingrese la descripción de la imagen aquí

mi idea es usar algo como enum como tipo para @IBInspectable, pero parece que no es el caso, ¿alguna idea de cómo implementar un elemento como este?

EDITAR:

Parece que @IBInspectablesolo admite estos tipos:

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

gorrón

ignotusverum
fuente
Una solución alternativa, de algún tipo, es poner una propiedad calculada inspeccionable delante del valor que desea establecer. Por supuesto, todavía no aparecerá mágicamente como un menú emergente de valores enumerados en Interface Builder; pero al menos puede definir un valor inspeccionable y usarlo para establecer una enumeración.
Matt
8
En la WWDC de este año, le pregunté a un ingeniero de Apple en el laboratorio de Herramientas para desarrolladores sobre esto. Dijo que estaba de acuerdo en que sería una gran característica, pero que actualmente no es posible. Me sugirió que archivara un radar en bugreport.apple.com, lo cual hice. Se cerró como un duplicado del número 15505220 pero recomendaría encarecidamente a las personas que presenten problemas similares. Estas cosas a menudo se resuelven si suficientes personas se quejan.
Will Clarke
1
¿En qué se diferencia esta pregunta de stackoverflow.com/questions/27432736/…
SwiftArchitect
Posible duplicado de Cómo crear un IBInspectable de tipo enumeración
SwiftArchitect
Y con SwiftUI y el nuevo Canvas en Xcode 11, parece que esto nunca estará en la hoja de ruta de Apple
Ben Leggiero

Respuestas:

26

Eso no es posible (por ahora). Solo puede usar los tipos que ve en la sección Atributos de tiempo de ejecución definidos por el usuario .

Del documento de Apple :

Puede adjuntar el atributo IBInspectable a cualquier propiedad en una declaración de clase, extensión de clase o categoría para cualquier tipo que sea compatible con los atributos de tiempo de ejecución definidos por Interface Builder: booleano, entero o número de punto flotante, cadena, cadena localizada, rectángulo, punto, tamaño , color, rango y nulo.

yusuke024
fuente
2
Cuando quiero usar una enumeración, le doy a los valores de enumeración asignaciones explícitas (= 1, = 2, etc.). Y en el controlador, agrego una aserción si el valor de IB no es uno de los valores de la enumeración. Es molesto que las enumeraciones no sean compatibles, pero este truco las hace más utilizables, al menos.
Zev Eisenberg
Una enumeración es un número entero.
Amin Negm-Awad
23

Otra solución alternativa para esto es alterar cómo aparece una propiedad de enumeración en el constructor de interfaces. Por ejemplo:

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

Esto asume una enumeración llamada FontWeight. Se basa en el hecho de que las enumeraciones y sus valores enteros sin procesar se pueden usar indistintamente en Objective-C. Después de hacer esto, puede especificar un número entero en el generador de interfaces para la propiedad que no es ideal, pero funciona y conserva una pequeña cantidad de seguridad de tipos cuando se usa la misma propiedad mediante programación.

Esta es una mejor alternativa que declarar una propiedad de entero separada porque no necesita escribir lógica adicional para manejar una segunda propiedad de entero que también podría usarse para lograr lo mismo.

Sin embargo, esto no funciona con Swift porque no podemos convertir implícitamente de un entero a una enumeración. Cualquier pensamiento sobre la solución sería apreciado.

Anthony Mattox
fuente
2
Realmente pensé que esto funcionaba en Swift, pero en más pruebas ... no
Dan Rosenstark
7

Hago esto usando un valor Inspectable NSInteger y anulo el setter para permitirle establecer la enumeración. Esto tiene la limitación de no usar una lista emergente y si cambia los valores de enumeración, las opciones de la interfaz no se actualizarán para coincidir.

Ejemplo.

En archivo de encabezado:

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

...

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

En el archivo de implementación:

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

Opcionalmente, puede agregar algo de lógica allí para asegurarse de que se establezca en un valor válido

Matthew Cawley
fuente
No estoy seguro de por qué se votó en contra. Parece una buena forma de solucionar el problema.
Fogmeister
Gracias @Fogmeister. De hecho, estoy usando esta implementación dentro de una aplicación en este momento :)
Matthew Cawley
4

Sikhapol es correcto, las enumeraciones aún no son compatibles y tampoco en xCode 9. Creo que el enfoque más seguro es usar enumeraciones como cadenas e implementar una var IBInspectable "sombra" (privada). Aquí hay un ejemplo de un elemento BarBtnPaintCode que representa un elemento de botón de barra que se puede diseñar con un icono personalizado (que se hace con PaintCode) dentro de Interface Builder (swift 4).

En la construcción de la interfaz, simplemente ingrese la cadena (idéntica al valor de enumeración), lo que lo mantiene claro (si está ingresando números, nadie sabe lo que significan)

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
        }

    }

}
HixField
fuente
Fantástica solución, implementada con Swift 4 sin problemas. si usa esto, tenga cuidado con los tipos de ortografía correctamente en guiones gráficos y xibs
MindBlower3
1

Como respondió @sikhapol, esto no es posible. La solución alternativa que utilizo para esto es tener un montón de IBInspectablebools en mi clase y simplemente seleccionar uno en el generador de interfaces. Para mayor seguridad de que no se configuran varios, agregue un NSAsserten el configurador para cada uno.

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

Esto es un poco tedioso y un poco descuidado en mi opinión, pero es la única forma de lograr este tipo de comportamiento que se me ocurre.

Chris
fuente
1

Quiero agregar que los identificadores de an enumno están disponibles en tiempo de ejecución para nadie en Objective-C. Por lo tanto, no puede haber la posibilidad de mostrarlo en cualquier lugar.

Amin Negm-Awad
fuente
1

Mi solución fue hacer:

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

En el propio IB, deberá establecer un int en el campo keyboardType

Gal Marom
fuente