@IBInspectable mit Aufzählung?

85

Ich möchte ein @IBInspectableElement erstellen , wie Sie auf dem Bild unten sehen:

Geben Sie hier die Bildbeschreibung ein

Meine Idee ist es, so etwas wie enum als Typ zu verwenden @IBInspectable, aber es sieht so aus, als ob es nicht der Fall ist. Gibt es Ideen, wie man ein solches Element implementiert?

BEARBEITEN:

Es sieht so aus, als ob @IBInspectablenur diese Typen unterstützt werden:

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

Mist

ignotusverum
quelle
Eine Art Problemumgehung besteht darin, eine überprüfbare berechnete Eigenschaft vor den Wert zu stellen, den Sie festlegen möchten. Natürlich wird es im Interface Builder immer noch nicht auf magische Weise als Popup-Menü mit aufgezählten Werten angezeigt. Zumindest können Sie jedoch einen überprüfbaren Wert definieren und damit eine Aufzählung festlegen.
Matt
8
Auf der diesjährigen WWDC habe ich einen Apple-Ingenieur im Developer Tools-Labor danach gefragt. Er sagte, dass er zustimmte, dass es eine großartige Funktion sein würde, aber es ist derzeit nicht möglich. Er schlug vor, unter bugreport.apple.com ein Radar einzureichen, was ich auch tat. Es wurde als Duplikat der Ausgabe 15505220 geschlossen, aber ich würde Leuten, die ähnliche Ausgaben einreichen, wärmstens empfehlen. Diese Dinge werden oft angesprochen, wenn sich genug Leute beschweren.
Will Clarke
1
Wie unterscheidet sich diese Frage von stackoverflow.com/questions/27432736/…
SwiftArchitect
Mögliches Duplikat von So erstellen Sie eine IBInspectable vom Typ enum
SwiftArchitect
Und mit SwiftUI und dem neuen Canvas in Xcode 11 scheint dies niemals auf Apples Roadmap zu stehen
Ben Leggiero

Antworten:

26

Das ist (vorerst) nicht möglich. Sie können nur die Typen verwenden, die im Abschnitt Benutzerdefinierte Laufzeitattribute angezeigt werden .

Aus Apples Dokument :

Sie können das IBInspectable-Attribut an jede Eigenschaft in einer Klassendeklaration, Klassenerweiterung oder Kategorie für jeden Typ anhängen, der von den vom Interface Builder definierten Laufzeitattributen unterstützt wird: Boolesche Zahl, Ganzzahl- oder Gleitkommazahl, Zeichenfolge, lokalisierte Zeichenfolge, Rechteck, Punkt, Größe , Farbe, Reichweite und Null.

yusuke024
quelle
2
Wenn ich eine Aufzählung verwenden möchte, gebe ich den Aufzählungswerten explizite Zuweisungen (= 1, = 2 usw.). Und im Handler füge ich eine Behauptung hinzu, wenn der Wert von IB nicht einer der Werte aus der Aufzählung ist. Es ist ärgerlich, dass Aufzählungen nicht unterstützt werden, aber dieser Trick macht sie zumindest benutzerfreundlicher.
Zev Eisenberg
Eine Aufzählung ist eine ganze Zahl.
Amin Negm-Awad
23

Eine andere Problemumgehung besteht darin, zu ändern, wie eine Aufzählungseigenschaft dem Interface Builder angezeigt wird. Beispielsweise:

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

Dies setzt eine Aufzählung mit dem Namen FontWeight voraus. Es beruht auf der Tatsache, dass Aufzählungen und ihre rohen ganzzahligen Werte in Objective-C etwas austauschbar verwendet werden können. Anschließend können Sie im Interface Builder eine Ganzzahl für die Eigenschaft angeben, die nicht ideal ist, aber funktioniert und bei programmgesteuerter Verwendung derselben Eigenschaft ein geringes Maß an Typensicherheit beibehält.

Dies ist eine bessere Alternative als das Deklarieren einer separaten Integer-Eigenschaft, da Sie keine zusätzliche Logik schreiben müssen, um eine zweite Integer-Eigenschaft zu verarbeiten, mit der auch dasselbe erreicht werden kann.

Dies funktioniert jedoch nicht mit Swift, da wir nicht implizit von einer Ganzzahl in eine Aufzählung umwandeln können. Irgendwelche Gedanken zur Lösung wären willkommen.

Anthony Mattox
quelle
2
Ich dachte wirklich, dass dies in Swift funktioniert, aber in weiteren Tests ... nein
Dan Rosenstark
7

Ich mache dies mit einem Inspectable NSInteger-Wert und überschreibe den Setter, damit er die Aufzählung setzen kann. Dies hat die Einschränkung, dass keine Popup-Liste verwendet wird. Wenn Sie Ihre Aufzählungswerte ändern, werden die Schnittstellenoptionen nicht entsprechend aktualisiert.

Beispiel.

In der Header-Datei:

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

...

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

In der Implementierungsdatei:

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

Sie können dort optional eine Logik hinzufügen, um sicherzustellen, dass sie auf einen gültigen Wert gesetzt wird

Matthew Cawley
quelle
Ich bin mir nicht sicher, warum dies abgelehnt wurde. Scheint ein guter Weg zu sein, um das Problem zu umgehen.
Fogmeister
Vielen Dank @Fogmeister - Ich verwende diese Implementierung derzeit in einer App :)
Matthew Cawley
4

Sikhapol ist korrekt, Aufzählungen werden auch in xCode 9 noch nicht unterstützt. Ich glaube, der sicherste Ansatz besteht darin, Aufzählungen als Zeichenfolgen zu verwenden und eine "private" IBInspectable-Variable "shadow" zu implementieren. Hier ist ein Beispiel für ein BarBtnPaintCode-Element, das ein Barbutton-Element darstellt, das mit einem benutzerdefinierten Symbol (das mit PaintCode erstellt wird) direkt im Interface Builder (Swift 4) gestaltet werden kann.

Beim Erstellen von Schnittstellen geben Sie einfach die Zeichenfolge ein (identisch mit dem Aufzählungswert), wodurch diese klar bleibt (wenn Sie Zahlen eingeben, weiß niemand, was sie bedeuten).

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
quelle
Fantastische Lösung, implementiert mit Swift 4 keine Probleme. Wenn Sie dies verwenden, achten Sie auf die korrekte Schreibweise in Storyboards und Xibs
MindBlower3
1

Wie @sikhapol antwortete, ist dies nicht möglich. Die Problemumgehung, die ich dafür verwende, besteht darin, eine Reihe von IBInspectableBools in meiner Klasse zu haben und nur einen im Interface Builder auszuwählen. Fügen Sie für zusätzliche Sicherheit, dass nicht mehrere festgelegt sind, NSAssertfür jeden einen im Setter hinzu.

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

Dies ist ein wenig langweilig und ein bisschen schlampig IMO, aber es ist der einzige Weg, um diese Art von Verhalten zu erreichen, die ich mir vorstellen kann

Chris
quelle
1

Ich möchte hinzufügen, dass die Bezeichner von enumzur Laufzeit für niemanden in Objective-C verfügbar sind. Es kann also keine Möglichkeit geben, es irgendwo anzuzeigen.

Amin Negm-Awad
quelle
1

Meine Lösung war zu tun:

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

Auf dem IB selbst müssen Sie im Feld keyboardType ein int festlegen

Gal Marom
quelle