@IBInspectable с enum?

86

Я хочу создать @IBInspectableэлемент, как вы видите на картинке ниже:

введите описание изображения здесь

Моя идея состоит в том, чтобы использовать что-то вроде перечисления в качестве типа @IBInspectable, но похоже, что это не так, есть идеи, как реализовать такой элемент?

РЕДАКТИРОВАТЬ:

Похоже, @IBInspectableподдерживает только эти типы:

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

облом

игнотусверум
источник
В некотором роде обходной путь заключается в том, чтобы поместить проверяемое вычисляемое свойство перед значением, которое вы хотите установить. Конечно, он все равно не появится волшебным образом в виде всплывающего меню перечисленных значений в Интерфейсном Разработчике; но по крайней мере вы можете определить проверяемое значение и использовать его для установки перечисления.
Мэтт
8
В этом году на WWDC я спросил об этом инженера Apple в лаборатории инструментов разработчика. Он сказал, что согласен, что это будет отличная функция, но в настоящее время это невозможно. Он предложил мне зарегистрировать радар на bugreport.apple.com, что я и сделал. Он был закрыт как дубликат проблемы 15505220, но я очень рекомендую людям, которые задают похожие проблемы. Эти вопросы часто решаются, если достаточно людей жалуется.
Will Clarke
1
чем этот вопрос отличается от stackoverflow.com/questions/27432736/…
SwiftArchitect
Возможный дубликат раздела Как создать IBInspectable типа enum
SwiftArchitect
А с SwiftUI и новым Canvas в Xcode 11 кажется, что этого никогда не будет в планах Apple,
Бен Легжеро

Ответы:

26

Это невозможно (пока). Вы можете использовать только те типы, которые вы видите в разделе « Пользовательские атрибуты времени выполнения ».

От компании Apple документа :

Вы можете прикрепить атрибут IBInspectable к любому свойству в объявлении класса, расширении класса или категории для любого типа, который поддерживается определенными атрибутами времени выполнения Interface Builder: логическое, целое число или число с плавающей запятой, строка, локализованная строка, прямоугольник, точка, размер , цвет, диапазон и ноль.

yusuke024
источник
2
Когда я хочу использовать перечисление, я даю значениям перечисления явные назначения (= 1, = 2 и т. Д.). И в обработчике я добавляю утверждение, если значение из IB не является одним из значений из перечисления. Раздражает то, что перечисления не поддерживаются, но этот трюк, по крайней мере, делает их более удобными.
Зев Айзенберг,
Перечисление - это целое число.
Амин Негм-Авад,
23

Другой способ решения этой проблемы - изменить способ отображения свойства перечисления для построителя интерфейса. Например:

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

Это предполагает наличие перечисления FontWeight. Он основан на том факте, что перечисления и их необработанные целочисленные значения могут использоваться в Objective-C как синонимы. После этого вы можете указать целое число в построителе интерфейса для свойства, которое не идеально, но работает и сохраняет небольшую степень безопасности типов при программном использовании того же свойства.

Это лучшая альтернатива, чем объявление отдельного целочисленного свойства, поскольку вам не нужно писать дополнительную логику для обработки второго целочисленного свойства, которое также можно использовать для достижения того же результата.

Однако это не работает со Swift, потому что мы не можем неявно приводить целое число к перечислению. Любые мысли о решении, которые будут оценены.

Энтони Маттокс
источник
2
Я действительно думал, что это сработало в Swift, но при дальнейшем тестировании ... нет
Дэн Розенстарк 01
7

Я делаю это, используя значение Inspectable NSInteger, и переопределяю установщик, чтобы он мог устанавливать перечисление. Это имеет ограничение: не использовать всплывающий список, и если вы измените значения перечисления, то параметры интерфейса не будут обновляться для соответствия.

Пример.

В заголовочном файле:

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

...

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

В файле реализации:

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

При желании вы можете добавить туда некоторую логику, чтобы гарантировать, что для него установлено допустимое значение

Мэтью Коули
источник
Не уверен, почему это было отклонено. Похоже, хороший способ обойти проблему.
Fogmeister
Спасибо @Fogmeister - я на самом деле использую эту реализацию внутри приложения в данный момент :)
Мэтью Коули
4

Sikhapol верен, перечисления еще не поддерживаются, также не поддерживаются в xCode 9. Я считаю, что самый безопасный подход - использовать перечисления как строки и реализовать «теневую» (частную) IBInspectable var. Вот пример элемента BarBtnPaintCode, который представляет собой элемент barbutton, который можно стилизовать с помощью настраиваемого значка (что делается с помощью PaintCode) прямо внутри Interface Builder (swift 4).

В сборке интерфейса вы просто вводите строку (идентичную значению перечисления), которая сохраняет ее ясной (если вы вводите числа, никто не знает, что они означают)

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
источник
Фантастическое решение, без проблем реализованное на Swift 4. если вы используете это, будьте осторожны с типами правописания в раскадровках и xibs
MindBlower3
1

Как ответил @sikhapol, это невозможно. Обходной путь, который я использую для этого, - иметь кучу IBInspectablebools в моем классе и просто выбрать один в построителе интерфейса. Для дополнительной безопасности, что несколько единиц не установлены, добавьте NSAssertв сеттер для каждого из них.

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

Это немного утомительно и немного небрежно ИМО, но это единственный способ добиться такого поведения, о котором я могу думать.

Крис
источник
1

Я хочу добавить, что идентификаторы объекта enumнедоступны во время выполнения для кого-либо в Objective-C. Так что не может быть возможности его где-либо отобразить.

Амин Негм-Авад
источник
1

Мое решение было сделать:

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

На самом IB вам нужно будет установить int в поле keyboardType

Гал Маром
источник