Какой-то глобальный для IBDesignables?

В раскадровке есть UIView с ограничением, например, пропорциональной высотой «0,1».

Представьте, что у вас есть аналогичное ограничение «0,1» для просмотров во многих разных сценах.

Допустим, вы хотите изменить значение с 0,1 на 0,975.

Вы должны изменить его индивидуально везде.

Есть ли способ создать своего рода "глобальное значение", используя @IBInspectable и/или ограничения?

Чтобы вы могли изменить их все сразу и увидеть все результаты сразу в раскадровке.

(Конечно, во время выполнения вы можете распространять их в коде. Но лучше увидеть это на раскадровке.)


Обратите внимание, например, что приведенное ниже решение Уэйна работает отлично: вы можете установить значение один раз и применить его везде в приложении. Но, к сожалению, он делает это только во время выполнения, вы не видите этого на раскадровке.


person Fattie    schedule 23.06.2016    source источник
comment
Возможно, я неправильно истолковываю ваш вопрос, но не могли бы вы просто ограничить высоту двух представлений контейнера относительно друг друга в одном контроллере представления? Не могли бы вы привести более конкретный пример, если я неправильно понял?   -  person William Anderson    schedule 23.06.2016
comment
Я думаю, что понял. Вы пытались создать класс контроллера представления IBDesignable с этим ограничением и использовать его в качестве базового класса для вашего UIViews? Меня тоже очень интересует, возможно ли это.   -  person William Anderson    schedule 23.06.2016
comment
Что говорится в сообщении об ошибке?   -  person William Anderson    schedule 23.06.2016
comment
Я добавил большую награду! Я очистил некоторые из старых комментариев здесь, ребята. Будем надеяться привлечь к этому внимание   -  person Fattie    schedule 02.07.2016
comment
Очень жаль, что нет действительно хорошего решения этой проблемы!! Может быть, я попробую задать более целенаправленный вопрос, возможно, появятся другие идеи. Спасибо всем!! (Я щелкнул вознаграждение за ответ с наибольшим количеством голосов, чтобы робот не потратил его впустую, ура) Конечно, Apple следует добавить эту функцию в будущем, это кажется настолько очевидным.   -  person Fattie    schedule 07.07.2016


Ответы (3)


Я не пробовал, и это только что набрано здесь, так что извините за опечатки, но я бы подумал о создании расширения для NSLayoutConstraint, что-то вроде:

let constants = [
    "bannerHeight" : CGFloat(50)
]

extension NSLayoutConstraint {
func setCommonConstant(name: String) {
    self.constant = constants[name]!
}
}

а затем с использованием определенных пользователем атрибутов времени выполнения в Xcode, поэтому укажите имя для использования:

commonConstant, type String, value name

Итак, вот так, для каждого ограничения в любом месте раскадровки:

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

person Wain    schedule 23.06.2016
comment
Вы имеете в виду, потому что это происходит во время выполнения, а не во время разработки / в раскадровке? - person Wain; 30.06.2016
comment
Это работает, хотя в этом коде много опечаток, немного улучшил его. - person Wain; 30.06.2016
comment
Да, это правильно. Другой ответ интересен, возможно, оба они могут быть объединены, чтобы добавить проверяемые ограничения, чтобы вы могли выбрать какой-либо пресет. Я не вижу способа, которым вы можете изменить один параметр в IB и автоматически обновить все ограничения для разрозненных представлений... - person Wain; 02.07.2016
comment
Хорошо, я наконец получил это! Это работает отлично, спасибо, я понятия не имел, что вы можете это сделать. Однако, к сожалению, к сожалению, это не обновляется в раскадровке .... только во время выполнения! Если бы только был способ заставить это работать мгновенно, как IBInspectable!! - person Fattie; 02.07.2016
comment
Да, я не понимаю, как их объединить и заставить работать над несколькими элементами одновременно, потому что возможность проектирования основана на экземплярах. Возможно, единственным другим способом был бы подкласс ограничений. Возможно, это можно использовать с проектируемым, хотя константа все равно должна быть в коде. - person Wain; 02.07.2016
comment
Верно. В самом деле, не было бы проблемы, если бы константа действительно была в коде — вы могли бы просто изменить ее там. - person Fattie; 02.07.2016

Я не могу придумать способ заставить IB обновлять все «экземпляры» во время разработки, когда в одном экземпляре изменяется значение свойства (и я думаю, что это, как правило, нежелательное и нестандартное поведение). Я совершенно уверен, что это невозможно сделать, потому что я не думаю, что IB хранит экземпляр вашего пользовательского представления в памяти на время сеанса IB. Я думаю, что он создает экземпляр представления, устанавливает для него свойства, делает снимок для рендеринга, а затем выпускает его.

Должна быть возможность настроить свойства, которые устанавливают значения «по умолчанию», которые будут использоваться будущими экземплярами вашего представления. Мы можем добиться этого, сохранив значения «по умолчанию» в NSUserDefaults (XCode) и прочитав значение по умолчанию внутри initWithFrame:, которое IB вызовет для инициализации нового экземпляра. Мы можем скрыть все эти стандартные вещи, используя #if TARGET_INTERFACE_BUILDER, чтобы они не отображались во время выполнения на устройстве.

IB_DESIGNABLE
@interface CoolView : UIView

@property (strong, nonatomic) IBInspectable UIColor* frameColor;

#if TARGET_INTERFACE_BUILDER
@property (strong, nonatomic) IBInspectable UIColor* defaultFrameColor;
#endif

@end

@implementation CoolView

- (id) initWithFrame:(CGRect)frame
{
    self = [super initWithFrame: frame];
    if ( self != nil ) {

#if TARGET_INTERFACE_BUILDER
        NSData *colorData = [[NSUserDefaults standardUserDefaults] objectForKey: @"defaultCoolViewFrameColor"];
        if ( colorData != nil ) {
            self.frameColor = [NSKeyedUnarchiver unarchiveObjectWithData: colorData];;
        }
#endif

    }
    return self;
}

- (void) drawRect:(CGRect)rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetLineWidth(context, 10);
    CGRect f = CGRectInset(self.bounds, 10, 10);
    [self.frameColor set];
    UIRectFrame(f);
}

#if TARGET_INTERFACE_BUILDER
- (void) setDefaultFrameColor:(UIColor *)defaultFrameColor
{
    _defaultFrameColor = defaultFrameColor;

    NSData *colorData = [NSKeyedArchiver archivedDataWithRootObject: defaultFrameColor];
    [[NSUserDefaults standardUserDefaults] setObject:colorData forKey:@"defaultCoolViewFrameColor"];
}
#endif

@end

Вероятно, вы могли бы приблизиться к своей первоначальной цели (обновление всех «экземпляров» в IB), если вы не против заставить IB полностью перезагрузить файл xib/storyboard. Для этого вам, вероятно, придется использовать описанную выше технику и расширить ее, включив код в пользовательский метод initWithCoder: в вашем представлении.

Возможно, вы могли бы стать действительно хитрым и попытаться «прикоснуться» к редактируемому файлу xib, и это может привести к перезагрузке IB?

person TomSwift    schedule 05.07.2016
comment
Сложный. Надо будет это расследовать! - person Fattie; 06.07.2016
comment
Я не могу придумать, как заставить IB обновлять все экземпляры во время разработки... это хорошая мысль. Итак, когда у вас есть ab IBDesignable, и вы меняете код (даже тривиально), он действительно повторно компилирует всю раскадровку. Поэтому я действительно думаю, что единственный способ добиться того, о чем я говорю, это действительно иметь значение в файле кода и изменить его. (Это по-прежнему очень удобно, намного лучше, чем возиться с изменением 100 IBInspectables или ограничений.) - person Fattie; 07.07.2016

Я пытался найти что-то подобное, когда начал разрабатывать с помощью Storyboard и Interface Builder. К сожалению, xCode не так масштабируем, а централизацию кода с помощью Interface Builder реализовать нелегко, как это можно сделать, например, при разработке для Android.

Вы можете попробовать что-то вроде этого, использовать пользовательский атрибут @IBDesignable в расширении UIView и изменить значения ограничений или добавить ограничение во время выполнения (но я не знаю, повлияет ли это также на Interface Builder):

extension UIView {
    @IBInspectable var insertTopConstraint: Bool {
        set {
            // suppose 10 is your fixed value
            addConstraint(NSLayoutConstraint(item: self, attribute: .Top, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 10))
            updateConstraintsIfNeeded() // could be useful..
        }
        get {
            return true
        }
    }
}

Это может быть отправной точкой. Надеюсь, поможет

person lubilis    schedule 23.06.2016
comment
Это работает для изменений пользовательского интерфейса при просмотре, и они видны в IB, для ограничений, которые я не пробовал и мог ошибиться. В противном случае вы можете централизованно поддерживать значение ограничения, используя этот подход. - person lubilis; 23.06.2016
comment
Inspectable borderColor был для меня только утилитой, и я использую ее, потому что мне нужны разные цвета. Если вам нужно реализовать его для ограничения, вам не придется изменять его во всех местах, вы просто устанавливаете insertTopConstraint = true в IB, и если вы хотите изменить константу ограничения для всех затронутых представлений, вы меняете ее только в проверяемом коде - person lubilis; 23.06.2016
comment
У вас, кажется, есть правильная идея, и я надеюсь, что кто-то умнее меня сможет это понять !!! :) - person Fattie; 30.06.2016