@IBInspectable con enum?


87

Vorrei creare l' @IBInspectableelemento come vedi nell'immagine qui sotto:

inserisci qui la descrizione dell'immagine

la mia idea è di usare qualcosa come enum come tipo per @IBInspectable, ma sembra che non sia il caso, qualche idea su come implementare un elemento come questo?

MODIFICARE:

Sembra che @IBInspectablesupporti solo questi tipi:

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

peccato


Una sorta di soluzione alternativa consiste nell'inserire una proprietà calcolata ispezionabile davanti al valore che si desidera impostare. Naturalmente, non apparirà ancora come un menu a comparsa di valori enumerati in Interface Builder; ma almeno puoi definire un valore ispezionabile e usarlo per impostare un enum.
opaco

8
Al WWDC di quest'anno, ho chiesto informazioni a un ingegnere Apple nel laboratorio Strumenti per sviluppatori. Ha detto che era d'accordo che sarebbe stata una grande caratteristica, ma al momento non è possibile. Mi ha suggerito di presentare un radar su bugreport.apple.com, cosa che ho fatto. È stato chiuso come duplicato del numero 15505220 ma consiglio vivamente a persone che inseriscono problemi simili. Queste cose spesso vengono risolte se un numero sufficiente di persone si lamenta.
Will Clarke

1
come è questa questione diversa da stackoverflow.com/questions/27432736/...
SwiftArchitect


E con SwiftUI e il nuovo Canvas in Xcode 11, sembra che questo non sarà mai sulla roadmap di Apple
Ben Leggiero

Risposte:


27

Non è possibile (per ora). È possibile utilizzare solo i tipi visualizzati nella sezione Attributi di runtime definiti dall'utente .

Dal documento di Apple :

È possibile collegare l'attributo IBInspectable a qualsiasi proprietà in una dichiarazione di classe, estensione di classe o categoria per qualsiasi tipo supportato dagli attributi di runtime definiti da Interface Builder: booleano, numero intero o in virgola mobile, stringa, stringa localizzata, rettangolo, punto, dimensione , colore, gamma e zero.


2
Quando voglio usare un enum, do ai valori enum assegnazioni esplicite (= 1, = 2, ecc.). E nel gestore, aggiungo un'asserzione se il valore di IB non è uno dei valori dell'enumerazione. Fastidioso che le enumerazioni non siano supportate, ma questo trucco le rende almeno più utilizzabili.
Zev Eisenberg

Un enum è un numero intero.
Amin Negm-Awad

23

Un'altra soluzione a questo problema è modificare il modo in cui una proprietà di enumerazione appare al costruttore di interfacce. Per esempio:

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

Ciò presuppone un'enumerazione denominata FontWeight. Si basa sul fatto che le enumerazioni ei loro valori interi grezzi possono essere usati in modo intercambiabile in Objective-C. Dopo aver fatto ciò, è possibile specificare un numero intero in Interface builder per la proprietà che non è l'ideale, ma funziona e mantiene una piccola quantità di sicurezza dei tipi quando si utilizza la stessa proprietà a livello di programmazione.

Questa è un'alternativa migliore rispetto alla dichiarazione di una proprietà intera separata perché non è necessario scrivere logica aggiuntiva per gestire una seconda proprietà intera che potrebbe essere utilizzata anche per ottenere la stessa cosa.

Tuttavia, questo non funziona con Swift perché non siamo in grado di eseguire il cast implicito da un numero intero a un enum. Qualsiasi pensiero sulla soluzione che sarebbe apprezzato.


2
Pensavo davvero che funzionasse in Swift, ma in ulteriori test ... no
Dan Rosenstark

7

Lo faccio usando un valore NSInteger ispezionabile e sovrascrivo il setter per consentirgli di impostare l'enum. Questo ha la limitazione di non utilizzare un elenco popup e se modifichi i valori di enum, le opzioni dell'interfaccia non si aggiorneranno per corrispondere.

Esempio.

Nel file di intestazione:

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

...

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

Nel file di implementazione:

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

Facoltativamente, potresti aggiungere un po 'di logica per assicurarti che sia impostato su un valore valido


Non sono sicuro del motivo per cui questo è stato votato. Sembra un buon modo per aggirare il problema.
Fogmeister

Grazie @Fogmeister - Al momento sto utilizzando questa implementazione all'interno di un'app :)
Matthew Cawley

4

Sikhapol è corretto, le enumerazioni non sono ancora supportate anche non in xCode 9. Credo che l'approccio più sicuro sia usare le enumerazioni come stringhe e implementare una variabile IBInspectable "shadow" (privata). Ecco un esempio di un elemento BarBtnPaintCode che rappresenta un elemento barbutton che può essere abbinato a un'icona personalizzata (che viene eseguita utilizzando PaintCode) direttamente all'interno di Interface Builder (swift 4).

Nella build dell'interfaccia devi solo inserire la stringa (identica al valore enum), che lo mantiene chiaro (se stai inserendo numeri nessuno sa cosa significano)

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
        }

    }

}

Soluzione fantastica, implementata con Swift 4 senza problemi. se lo usi, fai attenzione ai tipi di ortografia correttamente negli storyboard e negli xibs
MindBlower3

1

Come ha risposto @sikhapol, questo non è possibile. La soluzione alternativa che uso per questo è avere un mucchio di IBInspectablebool nella mia classe e selezionarne uno nel generatore di interfacce. Per una maggiore sicurezza che non siano impostati più di uno, aggiungi un NSAssertsetter per ciascuno.

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

Questo è un po 'noioso e un po' sciatto IMO, ma è l'unico modo per realizzare questo tipo di comportamento a cui posso pensare


1

Voglio aggiungere che gli identificatori di un enumnon sono disponibili in fase di esecuzione per nessuno in Objective-C. Quindi non ci può essere la possibilità di visualizzarlo ovunque.


1

La mia soluzione era fare:

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

Sull'IB stesso, dovrai impostare un int nel campo keyboardType

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.