@IBInspectable with enum?

86

Chciałbym stworzyć @IBInspectableelement jak na poniższym obrazku:

wprowadź opis obrazu tutaj

moim pomysłem jest użycie czegoś takiego jak wyliczenie jako typ dla @IBInspectable, ale wygląda na to, że tak nie jest, jakieś pomysły, jak zaimplementować taki element?

EDYTOWAĆ:

Wygląda na to, że @IBInspectableobsługuje tylko te typy:

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

porażka

ignotusverum
źródło
Pewnym sposobem obejścia tego problemu jest umieszczenie możliwej do sprawdzenia obliczonej właściwości przed wartością, którą chcesz ustawić. Oczywiście nadal nie pojawi się magicznie jako wyskakujące menu z wyliczonymi wartościami w Interface Builder; ale przynajmniej możesz zdefiniować sprawdzalną wartość i użyć jej do ustawienia wyliczenia.
mat.
8
Na tegorocznym WWDC zapytałem o to inżyniera Apple w laboratorium Narzędzi dla programistów. Powiedział, że zgodził się, że będzie to świetna funkcja, ale obecnie nie jest to możliwe. Zasugerował, żebym zgłosił radar na bugreport.apple.com, co zrobiłem. Został zamknięty jako duplikat numeru 15505220, ale gorąco polecam ludziom wprowadzanie podobnych problemów. Te sprawy często są rozwiązywane, jeśli wystarczająco dużo ludzi narzeka.
Will Clarke
1
czym różni się to pytanie od stackoverflow.com/questions/27432736/ ...
SwiftArchitect
Możliwy duplikat sposobu tworzenia IBInspectable typu enum
SwiftArchitect
A dzięki SwiftUI i nowemu Canvas w Xcode 11 wydaje się, że nigdy nie będzie to na mapie drogowej Apple
Ben Leggiero

Odpowiedzi:

26

To nie jest możliwe (na razie). Możesz używać tylko tych typów, które widzisz w sekcji Atrybuty środowiska wykonawczego zdefiniowane przez użytkownika .

Z dokumentu Apple :

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

yusuke024
źródło
2
Gdy chcę użyć wyliczenia, podaję wartościom wyliczenia jawne przypisania (= 1, = 2 itd.). W module obsługi dodaję asercję, jeśli wartość z IB nie jest jedną z wartości z wyliczenia. Irytujące, że wyliczenia nie są obsługiwane, ale ta sztuczka sprawia, że ​​są przynajmniej bardziej użyteczne.
Zev Eisenberg
Wyliczenie to liczba całkowita.
Amin Negm-Awad
23

Innym obejściem tego problemu jest zmiana wyglądu właściwości wyliczenia w konstruktorze interfejsu. Na przykład:

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

Zakłada się wyliczenie o nazwie FontWeight. Opiera się na fakcie, że wyliczenia i ich surowe wartości całkowite mogą być używane nieco zamiennie w Objective-C. Po wykonaniu tej czynności możesz określić liczbę całkowitą w narzędziu do tworzenia interfejsu dla właściwości, która nie jest idealna, ale działa i zachowuje niewielki poziom bezpieczeństwa typów, gdy używasz tej samej właściwości programowo.

Jest to lepsza alternatywa niż deklarowanie oddzielnej właściwości całkowitej, ponieważ nie musisz pisać dodatkowej logiki do obsługi drugiej właściwości całkowitej, która mogłaby być również użyta do osiągnięcia tego samego.

Jednak to nie działa w Swift, ponieważ nie jesteśmy w stanie niejawnie rzutować z liczby całkowitej na wyliczenie. Wszelkie przemyślenia na temat rozwiązania tego będą mile widziane.

Anthony Mattox
źródło
2
Naprawdę myślałem, że to zadziałało w Swift, ale podczas dalszych testów ... nie
Dan Rosenstark
7

Robię to przy użyciu wartości Inspectable NSInteger i zastępuję metodę ustawiającą, aby umożliwić ustawienie wyliczenia. Ma to ograniczenie polegające na nieużywaniu listy podręcznej i jeśli zmienisz wartości wyliczenia, opcje interfejsu nie zostaną zaktualizowane, aby pasowały.

Przykład.

W pliku nagłówkowym:

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

...

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

W pliku wdrożeniowym:

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

Możesz opcjonalnie dodać tam logikę, aby upewnić się, że jest ustawiona na prawidłową wartość

Matthew Cawley
źródło
Nie wiem, dlaczego głosowano w dół. Wydaje się, że jest to dobry sposób na obejście problemu.
Fogmeister
Dziękuję @Fogmeister - aktualnie używam tej implementacji wewnątrz aplikacji :)
Matthew Cawley
4

Sikhapol jest poprawny, wyliczenia nie są jeszcze obsługiwane, również nie w xCode 9. Uważam, że najbezpieczniejszym podejściem jest użycie wyliczeń jako ciągów znaków i zaimplementowanie "shadow" (prywatnej) zmiennej IBInspectable. Oto przykład elementu BarBtnPaintCode, który reprezentuje element barbutton, który można stylizować za pomocą niestandardowej ikony (wykonanej za pomocą PaintCode) bezpośrednio w Interface Builder (swift 4).

W budowaniu interfejsu po prostu wpisujesz ciąg (identyczny z wartością wyliczenia), dzięki czemu jest jasne (jeśli wpisujesz liczby, nikt nie wie, co oznaczają)

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
źródło
Fantastyczne rozwiązanie, wdrożone z Swift 4 bez problemów. Jeśli używasz tego uważać typów pisowni poprawnie storyboardów i xibs
MindBlower3
1

Jak odpowiedział @sikhapol, nie jest to możliwe. Obejściem, którego używam w tym celu, jest posiadanie kilku IBInspectablebooli w mojej klasie i po prostu wybranie jednego w konstruktorze interfejsu. Dla dodatkowego bezpieczeństwa, że ​​nie ustawiono wielu z nich, dodaj NSAssertsetter dla każdego z nich.

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

To trochę żmudne i trochę niechlujne IMO, ale to jedyny sposób na osiągnięcie tego rodzaju zachowania, o którym przychodzi mi do głowy

Chris
źródło
1

Chcę dodać, że identyfikatory enumnie są dostępne w czasie wykonywania dla nikogo w Objective-C. Nie ma więc możliwości nigdzie go wyświetlać.

Amin Negm-Awad
źródło
1

Moim rozwiązaniem było:

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

W samym IB będziesz musiał ustawić int w polu keyboardType

Gal Marom
źródło