@IBInspectable com enum?

86

Eu gostaria de criar um @IBInspectableelemento como você pode ver na imagem abaixo:

insira a descrição da imagem aqui

minha idéia é usar algo como enum como tipo para @IBInspectable, mas parece que não é o caso, alguma idéia de como implementar um elemento como este?

EDITAR:

Parece que @IBInspectablesuporta apenas estes tipos:

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

vadio

Ignotusverum
fonte
Uma espécie de solução alternativa é colocar uma propriedade computada inspecionável antes do valor que você deseja definir. Claro, ele ainda não aparecerá magicamente como um menu pop-up de valores enumerados no Interface Builder; mas pelo menos você pode definir um valor inspecionável e usá-lo para definir um enum.
mate
8
Na WWDC deste ano, perguntei a um engenheiro da Apple no laboratório de Ferramentas para Desenvolvedores sobre isso. Ele disse que concordou que seria um ótimo recurso, mas não é possível no momento. Ele sugeriu que eu registrasse um radar em bugreport.apple.com, o que fiz. Ele foi fechado como uma duplicata do problema 15505220, mas eu recomendo que as pessoas coloquem problemas semelhantes. Essas coisas geralmente são resolvidas se um número suficiente de pessoas reclamar.
Will Clarke de
1
como esta pergunta é diferente de stackoverflow.com/questions/27432736/…
SwiftArchitect
Possível duplicata de How to create an IBInspectable do tipo enum
SwiftArchitect
E com o SwiftUI e o novo Canvas no Xcode 11, parece que isso nunca estará no roteiro da Apple
Ben Leggiero

Respostas:

26

Isso não é possível (por enquanto). Você só pode usar os tipos que vê na seção Atributos de tempo de execução definidos pelo usuário .

Do documento da Apple :

Você pode anexar o atributo IBInspectable a qualquer propriedade em uma declaração de classe, extensão de classe ou categoria para qualquer tipo que seja compatível com os atributos de tempo de execução definidos do Interface Builder: booleano, número inteiro ou de ponto flutuante, string, string localizada, retângulo, ponto, tamanho , cor, intervalo e nulo.

yusuke024
fonte
2
Quando desejo usar um enum, dou atribuições explícitas aos valores de enum (= 1, = 2 etc.). E no manipulador, adiciono uma afirmação se o valor de IB não for um dos valores do enum. É irritante que enums não sejam suportados, mas esse truque os torna mais utilizáveis, pelo menos.
Zev Eisenberg
Um enum é um número inteiro.
Amin Negm-Awad
23

Outra solução alternativa para isso é alterar como uma propriedade de enumeração aparece para o construtor de interface. Por exemplo:

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

Isso pressupõe um enum chamado FontWeight. Ele se baseia no fato de que enums e seus valores inteiros brutos podem ser usados ​​de forma intercambiável em Objective-C. Depois de fazer isso, você pode especificar um inteiro no construtor de interface para a propriedade que não é ideal, mas funciona e mantém uma pequena quantidade de segurança de tipo ao usar a mesma propriedade de forma programática.

Esta é uma alternativa melhor do que declarar uma propriedade inteira separada porque você não precisa escrever lógica extra para lidar com uma segunda propriedade inteira que também pode ser usada para realizar a mesma coisa.

No entanto, isso não funciona com o Swift porque não podemos converter implicitamente de um inteiro para um enum. Quaisquer pensamentos sobre a resolução que serão apreciados.

Anthony Mattox
fonte
2
Eu realmente pensei que isso funcionou em Swift, mas em testes adicionais ... não
Dan Rosenstark
7

Eu faço isso usando um valor NSInteger Inspectable e substituo o setter para permitir que ele defina o enum. Isso tem a limitação de não usar uma lista pop-up e, se você alterar os valores enum, as opções da interface não serão atualizadas para corresponder.

Exemplo.

No arquivo de cabeçalho:

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

...

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

No arquivo de implementação:

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

Você pode opcionalmente adicionar alguma lógica lá para garantir que está sendo definido com um valor válido

Matthew Cawley
fonte
Não tenho certeza por que isso foi rejeitado. Parece uma boa forma de contornar o problema.
Fogmeister de
Obrigado @Fogmeister - Na verdade, estou usando essa implementação dentro de um aplicativo no momento :)
Matthew Cawley
4

Sikhapol está correto, enums ainda não são suportados também não no xCode 9. Eu acredito que a abordagem mais segura é usar enums como strings e implementar uma "sombra" (privada) IBInspectable var. Aqui está um exemplo de um item BarBtnPaintCode que representa um item Barbutton que pode ser estilizado com um ícone personalizado (isso é feito usando PaintCode) dentro do Interface Builder (swift 4).

Na construção da interface, você apenas insere a string (idêntica ao valor enum), que o mantém claro (se você está inserindo números, ninguém sabe o que eles significam)

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
fonte
Solução fantástica, implementada com Swift 4 sem problemas. se você usar isso, tome cuidado com os tipos de grafia corretamente em storyboards e xibs
MindBlower3
1

Como @sikhapol respondeu, isso não é possível. A solução que uso para isso é ter um monte de IBInspectablebools em minha classe e apenas selecionar um no construtor de interface. Para aumentar a segurança de que vários não sejam configurados, adicione um NSAssertno configurador para cada um.

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

Isso é um pouco entediante e um pouco desleixado OMI, mas é a única maneira de realizar esse tipo de comportamento que eu consigo pensar

Chris
fonte
1

Quero acrescentar que os identificadores de um enumnão estão disponíveis em tempo de execução para ninguém em Objective-C. Portanto, não pode haver a possibilidade de exibi-lo em qualquer lugar.

Amin Negm-Awad
fonte
1

Minha solução foi fazer:

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

No próprio IB, você precisará definir um int no campo keyboardType

Gal Marom
fonte