Jak utworzyć IBInspectable typu wyliczenie


83

enumto nie atrybut definiuje czas pracy interfejsu Builder. Poniższe informacje nie są wyświetlane w Inspektorze atrybutów programu Interface Builder:

enum StatusShape:Int {
    case Rectangle = 0
    case Triangle = 1
    case Circle = 2
}
@IBInspectable var shape:StatusShape = .Rectangle

Z dokumentacji: Możesz dołączyć atrybut IBInspectable do dowolnej właściwości w deklaracji klasy, rozszerzeniu klasy lub kategorii dla dowolnego typu, który jest obsługiwany przez zdefiniowane w programie Interface Builder atrybuty środowiska wykonawczego: wartość logiczna, liczba całkowita lub zmiennoprzecinkowa, ciąg, zlokalizowany ciąg, prostokąt , punkt, rozmiar, kolor, zakres i zero.

P: Jak mogę zobaczyć enumInspektor atrybutów w Interface Builder?


1
Gdzie jest wyliczenie na tej liście? Dlaczego myślisz, że możesz użyć wyliczenia?
— Paulw11,

13
Byłoby miło móc wybrać enumprzypadek prosto z IB, jak przypuszczam, lub a UIFont, tak jak natywne obiekty UIKit.
— SwiftArchitect

Odpowiedzi:


76

Szybki 3

@IBInspectable var shape:StatusShape = .Rectangle po prostu tworzy pusty wpis w Interface Builder:

Niedostępne w IB

Użyj adaptera, który będzie działał jako pomost między Swift a Interface Builder.
shapeAdaptermożna sprawdzić od IB:

   // IB: use the adapter
   @IBInspectable var shapeAdapter:Int {
        get {
            return self.shape.rawValue
        }
        set( shapeIndex) {
            self.shape = StatusShape(rawValue: shapeIndex) ?? .Rectangle
        }
    }

Dostępne w wersji IB

W przeciwieństwie do metody kompilacji warunkowej (przy użyciu #if TARGET_INTERFACE_BUILDER), typ shapezmiennej nie zmienia się wraz z celem, potencjalnie wymagając dalszych zmian w kodzie źródłowym, aby poradzić sobie z wersjami shape:NSIntegervs.shape:StatusShape

   // Programmatically: use the enum
   var shape:StatusShape = .Rectangle

Kompletny kod

@IBDesignable
class ViewController: UIViewController {

    enum StatusShape:Int {
        case Rectangle
        case Triangle
        case Circle
    }

    // Programmatically: use the enum
    var shape:StatusShape = .Rectangle

    // IB: use the adapter
    @IBInspectable var shapeAdapter:Int {
        get {
            return self.shape.rawValue
        }
        set( shapeIndex) {
            self.shape = StatusShape(rawValue: shapeIndex) ?? .Rectangle
        }
    }
}

► Znajdź to rozwiązanie na GitHub .


Dlaczego zachowujesz shapeAsIntjako własność przechowywaną, a nie obliczoną?
— nhgrif

Dobrze. Nie możesz jednak utworzyć właściwości writeonly... ale właściwość obliczona nie tworzy zmiennej instancji zapasowej, tak jak robi to twoja właściwość przechowywana.
— nhgrif

Dodano rozwiązanie bez magazynu zapasowego zaproponowane przez @nhgrif.
— SwiftArchitect

1
Przepraszam za mój głos przeciw. Myślałem, że mam działające rozwiązanie z TARGET_INTERFACE_BUILDER, ale zostałem wprowadzony w błąd. Przepraszamy, powodzenia w SO
— Dan Rosenstark

szybko do przodu do 2017 r. czy ten problem pozostaje ten sam?
— NoSixties

41

Zamiast ustawiać możliwe do sprawdzenia wyliczenia z wartościami int, możesz również ustawić je za pomocą ciągów. Chociaż nie jest tak preferowana jak lista rozwijana, przynajmniej ta opcja zapewnia pewien poziom czytelności.

Opcja tylko w wersji Swift:

// 1. Set up your enum
enum Shape: String {
    case Rectangle = "rectangle" // lowercase to make it case-insensitive
    case Triangle = "triangle"
    case Circle = "circle"
}


// 2. Then set up a stored property, which will be for use in code
var shape = Shape.Rectangle // default shape


// 3. And another stored property which will only be accessible in IB (because the "unavailable" attribute prevents its use in code)
@available(*, unavailable, message: "This property is reserved for Interface Builder. Use 'shape' instead.")
@IBInspectable var shapeName: String? {
    willSet {
        // Ensure user enters a valid shape while making it lowercase.
        // Ignore input if not valid.
        if let newShape = Shape(rawValue: newValue?.lowercased() ?? "") {
            shape = newShape
        }
    }
}

Możliwe jest również, aby to działało również z celem-c, dodając inicjator do wyliczenia. Jednak kompilator pokaże tylko błąd „niedostępny” dla właściwości tylko IB w kodzie Swift.

Opcja Swift z kompatybilnością Obj-C:

@objc enum Shape: Int {
    case None
    case Rectangle
    case Triangle
    case Circle

    init(named shapeName: String) {
        switch shapeName.lowercased() {
        case "rectangle": self = .Rectangle
        case "triangle": self = .Triangle
        case "circle": self = .Circle
        default: self = .None
        }
    }
}

var shape = Shape.Rectangle // default shape

@available(*, unavailable, message: "This property is reserved for Interface Builder. Use 'shape' instead.")
@IBInspectable var shapeName: String? {
    willSet {
        if let newShape = Shape(rawValue: newValue?.lowercased() ?? "") {
            shape = newShape
        }
    }
}

2
Podoba mi się ta Swift Onlywersja: pozwala na zwykły angielski w IB .
— SwiftArchitect

Czy ktoś wie, jak uzyskać opcje prezentowane jako lista rozwijana ciągów w IB przy użyciu tej metody?
— airowe

Chciałbym wiedzieć, jeśli to możliwe, jak to zrobić za pomocą rozwijanego menu.
— Wael

19

Nie pamiętam szybkiej składni, ale tak to rozwiązałem w obj-c

#if TARGET_INTERFACE_BUILDER
@property (nonatomic) IBInspectable NSInteger shape;
#else
@property (nonatomic) StatusShape shape;
#endif

1
Jedynym miejscem, w którym mam montaż warunkowy, jest to jedno miejsce w deklaracji. Brak asemblacji warunkowej nigdzie indziej, ponieważ wyliczenie jest wartością całkowitą w obj-c. Jedno miejsce do usunięcia, gdy Apple to naprawi (zakładając, że tak się stanie).
— Jason Bobier

To dowcipne i niesamowite rozwiązanie, o którym wspomniano tutaj nshipster.com/ibinspectable-ibdesignable
— Dan Rosenstark

7

Na rok 2020 - odpowiedź @SwiftArchitect zaktualizowana na dziś:

Oto typowy pełny przykład z całą dzisiejszą składnią

wprowadź opis obrazu tutaj

import UIKit

@IBDesignable class ClipLabels: UILabel {
    
    enum Side: Int { case left, right }
    
    var side: Side = .left {
        didSet {
            common()
        }
    }
    
    @available(*, unavailable, message: "IB only")
    @IBInspectable var leftRight01: Int {
        get {
            return self.side.rawValue
        }
        set(index) {
            self.side = Side(rawValue: index) ?? .left
        }
    }
    

i tylko przykład użycia ...

switch side {
    case .left:
        textColor = .red
    case .right:
        textColor = .green
    }

W przypadku tej krytycznej kontroli jakości Swift / iOS

• bardzo stara odpowiedź @SwiftArchitect jest całkowicie poprawna, ale

• Właśnie zaktualizowałem go i dodałem krytyczną „niedostępną” rzecz, która jest teraz możliwa w Swift.


4

To stary wątek, ale przydatny. Dostosowałem moją odpowiedź do Swift 4.0 i Xcode 9.0 - Swift 4 ma własne małe problemy z tym problemem. Mam zmienną @IBInspectable z typem wyliczenia i Xcode 9.0 nie jest zadowolony, pokazując mi tę „Właściwość nie może być oznaczona jako @IBInspectable, ponieważ jej typ nie może być reprezentowany w Objective-c”

@Eporediese częściowo rozwiązuje ten problem (dla swift3); używając właściwości dla scenorysu, ale prostego wyliczenia dla pozostałej części kodu. Poniżej znajduje się bardziej kompletny zestaw kodów, który zawiera właściwość do pracy w obu przypadkach.

enum StatusShape: Int {
  case Rectangle = 0
  case Triangle = 1
  case Circle = 2
}
var _shape:StatusShape = .Rectangle  // this is the backing variable

#if TARGET_INTERFACE_BUILDER
  @IBInspectable var shape: Int {    // using backing variable as a raw int

    get { return _shape.rawValue }
    set {
      if _shape.rawValue != newValue {
        _shape.rawValue = newValue
      }
    }
}
#else
var shape: StatusShape {  // using backing variable as a typed enum
  get { return _shape }
  set {
    if _shape != newValue {
      _shape = newValue
    }
  }
}
#endif

2

Rozwiązanie Swift 3 oparte na SwiftArchitect

enum StatusShape: Int {
    case rectangle, triangle, circle
}
var statusShape: StatusShape = .rectangle
#if TARGET_INTERFACE_BUILDER
@IBInspectable var statusShapeIB: Int {
    get { 
        return statusShape.rawValue 
    }
    set { 
        guard let statusShape = StatusShape(rawValue: newValue) else { return }
        self.statusShape = statusShape
    }
}   //convenience var, enum not inspectable
#endif

2
Jeśli umieścisz tę właściwość inspekcyjną IB w #if TARGET_INTERFACE_BUILDERbloku, wpłynie to tylko na renderowanie w programie Interface Builder, a nie w czasie wykonywania. Spowoduje to prawdopodobnie nieoczekiwane zachowanie i zawsze otrzymasz ostrzeżenie w konsoli:Failed to set (statusShapeIB) user defined inspected property ...: this class is not key value coding-compliant for the key statusShapeIB.
— Mischa
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.