Есть ли способ для Интерфейсного Разработчика представить представления IBDesignable, которые не переопределяют drawRect:

Я очень редко переопределяю drawRect в своих подклассах UIView, обычно предпочитая устанавливать layer.contents с предварительным рендерингом изображений и часто использованием нескольких подуровней или подпредставлений и управления ими на основе входных параметров. Существует ли путь к IB для рендеринга этих более сложных стопок представления?

62
задан 5 October 2014 в 01:55

8 ответов

Спасибо, @zisoft для clueing меня в на prepareForInterfaceBuilder. Там несколько нюансов с циклом рендеринга Интерфейсного Разработчика, которые были источником моих проблем и стоит отметить...

  1. Подтвержденный: Вы не должны использовать -drawRect.

Установка отображает на работах состояний управления UIButton. Произвольные стопки слоя, кажется, работают, если несколько вещей имеются в виду...

  1. использование IB initWithFrame:

.. не initWithCoder. awakeFromNib также НЕ назван.

  1. init... только назван однажды на сессию

Т.е. после того как на перекомпилировали каждый раз, когда Вы вносите изменение в файле. При изменении свойств IBInspectable init НЕ называют снова. Однако...

  1. prepareForInterfaceBuilder назван на каждом изменении свойства

, Оно похоже на наличие KVO на всем Вашем IBInspectables, а также других встроенных свойствах. Можно протестировать это сами при наличии Вашего _setup названный метод, сначала только от Вашего init.. метод. Изменение IBInspectable не будет иметь никакого эффекта. Затем добавьте вызов также к [1 110]. Whahla! Отметьте, для Вашего кода во время выполнения будет, вероятно, нужен некоторый дополнительный KVO, так как он не будет звонить prepareForIB метод. Больше на этом ниже...

  1. init... слишком рано тянуть, устанавливать содержание слоя, и т.д.

, По крайней мере, с моим UIButton, подкласс, звоня [self setImage:img forState:UIControlStateNormal] не имеет никакого эффекта в IB. Необходимо назвать его от [1 115] или через рычаг KVO.

  1. , Когда IB не удается представить, она не очищает наш Ваш компонент, а скорее сохраняет последнюю успешную версию.

Может сбивать с толку время от времени, когда Вы вносите изменения, которые не имеют никакого эффекта. Проверьте журналы сборки.

  1. <забастовка> Подсказка: Сохраните Монитор Действия поблизости

<забастовка> , я добираюсь, зависает все время на паре различных процессов поддержки, и они удаляют целую машину с ними. Подайте заявку Force Quit подробно.

(ОБНОВЛЕНИЕ: Это действительно не было верно, так как XCode6 вышел из беты. Это редко зависает больше)

, ОБНОВЛЕНИЮ

  1. 6.3.1, кажется, не нравится KVO в версии IB. Теперь Вам, кажется, нужен флаг, чтобы поймать Интерфейсного Разработчика и не настроить KVOs. Это в порядке как prepareForInterfaceBuilder метод эффективно KVOs весь эти IBInspectable свойства. Неудачно, что это поведение не зеркально отражается так или иначе во времени выполнения, таким образом требующем ручного KVO. См. обновленный пример кода ниже.

примером подкласса UIButton

Ниже является некоторый пример кода рабочего подкласса IBDesignable UIButton. Примечание ~~, prepareForInterfaceBuilder на самом деле не требуется, поскольку KVO прислушивается к изменениям в наших соответствующих свойствах и инициировал перерисовку. ОБНОВЛЕНИЕ ~~: Посмотрите точку 8 выше.

IB_DESIGNABLE
@interface SBR_InstrumentLeftHUDBigButton : UIButton

@property (nonatomic, strong) IBInspectable  NSString *topText;
@property (nonatomic) IBInspectable CGFloat topTextSize;
@property (nonatomic, strong) IBInspectable NSString *bottomText;
@property (nonatomic) IBInspectable CGFloat bottomTextSize;
@property (nonatomic, strong) IBInspectable UIColor *borderColor;
@property (nonatomic, strong) IBInspectable UIColor *textColor;

@end



@implementation HUDBigButton
{
    BOOL _isInterfaceBuilder;
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self _setup];

    }
    return self;
}

//---------------------------------------------------------------------

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        [self _setup];
    }
    return self;
}

//---------------------------------------------------------------------

- (void)_setup
{
    // Defaults.  
    _topTextSize = 11.5;
    _bottomTextSize = 18;
    _borderColor = UIColor.whiteColor;
    _textColor = UIColor.whiteColor;
}

//---------------------------------------------------------------------

- (void)prepareForInterfaceBuilder
{
    [super prepareForInterfaceBuilder];
    _isInterfaceBuilder = YES;
    [self _render];
}

//---------------------------------------------------------------------

- (void)awakeFromNib
{
    [super awakeFromNib];
    if (!_isInterfaceBuilder) { // shouldn't be required but jic...

        // KVO to update the visuals
        @weakify(self);
        [self
         bk_addObserverForKeyPaths:@[@"topText",
                                     @"topTextSize",
                                     @"bottomText",
                                     @"bottomTextSize",
                                     @"borderColor",
                                     @"textColor"]
         task:^(id obj, NSDictionary *keyPath) {
             @strongify(self);
             [self _render];
         }];
    }
}

//---------------------------------------------------------------------

- (void)dealloc
{
    if (!_isInterfaceBuilder) {
        [self bk_removeAllBlockObservers];
    }
}

//---------------------------------------------------------------------

- (void)_render
{
    UIImage *img = [SBR_Drawing imageOfHUDButtonWithFrame:self.bounds
                                                edgeColor:_borderColor
                                          buttonTextColor:_textColor
                                                  topText:_topText
                                              topTextSize:_topTextSize
                                               bottomText:_bottomText
                                       bottomTextSize:_bottomTextSize];

    [self setImage:img forState:UIControlStateNormal];
}

@end
133
ответ дан 31 October 2019 в 13:21

Этот ответ связан с переопределением drawRect, но возможно это может дать некоторое представление:

у меня есть пользовательский класс UIView, который имеет сложные рисунки в drawRect. Необходимо заботиться о ссылках, которые не доступны в течение времени проектирования, т.е. UIApplication. Для этого я переопределяю prepareForInterfaceBuilder, где я установил булев флаг, который я использую в drawRect для различения и время проектирования во время выполнения:

@IBDesignable class myView: UIView {
    // Flag for InterfaceBuilder
    var isInterfaceBuilder: Bool = false    

    override init(frame: CGRect) {
        super.init(frame: frame)
        // Initialization code
    }

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override func prepareForInterfaceBuilder() {
        self.isInterfaceBuilder = true
    }

    override func drawRect(rect: CGRect)
    {
        // rounded cornders
        self.layer.cornerRadius = 10
        self.layer.masksToBounds = true

        // your drawing stuff here

        if !self.isInterfaceBuilder {
            // code for runtime
            ...
        }

    }

}

вот то, как это смотрит в InterfaceBuilder:

enter image description here

15
ответ дан 31 October 2019 в 13:21

Вы не должны использовать drawRect, вместо этого можно создать пользовательский интерфейс в xib файле, загрузить его в initWithCoder и initWithFrame, и это будет живой рендеринг в IB после добавления IBDesignable. Проверьте это короткое учебное руководство: https://www.youtube.com/watch? v=L97MdpaF3Xg

9
ответ дан 31 October 2019 в 13:21

Я думаю, что layoutSubviews является самым простым механизмом.

Вот (намного) более простой пример в Swift:

@IBDesignable
class LiveLayers : UIView {

    var circle:UIBezierPath {
        return UIBezierPath(ovalInRect: self.bounds)
    }

    var newLayer:CAShapeLayer {
        let shape = CAShapeLayer()
        self.layer.addSublayer(shape)
        return shape
    }
    lazy var myLayer:CAShapeLayer = self.newLayer

    // IBInspectable proeprties here...
    @IBInspectable var pathLength:CGFloat = 0.0 { didSet {
        self.setNeedsLayout()
    }}

    override func layoutSubviews() {
        myLayer.frame = self.bounds // etc
        myLayer.path = self.circle.CGPath
        myLayer.strokeEnd = self.pathLength
    }
}

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

7
ответ дан 31 October 2019 в 13:21

В моем случае было две проблемы:

  1. я не реализовал initWithFrame в пользовательском представлении: (Обычно initWithCoder: назван, когда Вы инициализируете через IB, но по некоторым причинам initWithFrame:, необходим для IBDesignable только. Не назван во время времени выполнения, когда Вы реализуете через IB)

  2. , перо Моего пользовательского представления загружалось от mainBundle: [NSBundle bundleForClass:[self class]] был необходим.

4
ответ дан 31 October 2019 в 13:21

Для разработки ответ Karam Singh Хари этот слайд-шоу объясняет далее:

http://www.splinter.com.au/presentations/ibdesignable/

Затем, если Вы не видите, что Ваши изменения обнаруживаются в Интерфейсном Разработчике, попробуйте эти меню:

  • XCode-> Редактор-> Автоматически Представления Обновления
  • XCode-> Редактор-> Обновление Все Представления
  • XCode-> Редактор-> Отладка Выбранные Представления

, К сожалению, отлаживая мое представление заморозили XCode, но это должно работать на маленькие проекты (YMMV).

3
ответ дан 31 October 2019 в 13:21

Я полагаю, что можно реализовать prepareForInterfaceBuilder и сделать базовую работу анимации там, чтобы заставить это обнаруживаться в IB. Я сделал некоторые необычные вещи с подклассами UIButton, которые делают их собственную базовую работу слоя анимации для рисования границ или фонов, и они живут рендеринг в интерфейсном разработчике очень хорошо, таким образом, я воображаю, разделяете ли Вы UIView на подклассы непосредственно, затем prepareForInterfaceBuilder все, что необходимо будет сделать по-другому. Следует иметь в виду, хотя это, метод только когда-либо выполняется IB

, Отредактированным для включения кода, как требуется

, у меня есть что-то подобное, но не точно как это (извините, я не могу дать Вам, что я действительно делаю, но это - вещь работы)

class BorderButton: UIButton {
    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }

    func commonInit(){
        layer.borderWidth = 1
        layer.borderColor = self.tintColor?.CGColor
        layer.cornerRadius = 5    
    }

    override func tintColorDidChange() {
        layer.borderColor = self.tintColor?.CGColor
    }

    override var highlighted: Bool {
        willSet {
            if(newValue){
                layer.backgroundColor = UIColor(white: 100, alpha: 1).CGColor
            } else {
                layer.backgroundColor = UIColor.clearColor().CGColor
            }
        }
    }
}

, я переопределяю и initWithCoder и initWithFrame, потому что я хочу смочь использовать компонент в коде или в IB (и как другие ответы указывают, необходимо реализовать initWithFrame для создания IB счастливый.

Затем в commonInit я настроил базовый материал анимации, чтобы потянуть границу и сделать его симпатичным.

я также реализую willSet, чтобы выделенная переменная изменила цвет фона, потому что я ненавижу, когда кнопки тянут границы, но не обеспечивают обратную связь при нажатии (я ненавижу его, когда нажатая кнопка похожа на ненажатую кнопку)

2
ответ дан 31 October 2019 в 13:21

макрос Swift 3

#if TARGET_INTERFACE_BUILDER
#else
#endif

и класс с функцией, которая вызвана, когда IB представляет раскадровку

@IBDesignable
class CustomView: UIView
{
    @IBInspectable
    public var isCool: Bool = true {
        didSet {
            #if TARGET_INTERFACE_BUILDER

            #else

            #endif
        }
    }

    override func prepareForInterfaceBuilder() {
        // code
    }
}

, IBInspectable может использоваться с типами ниже

Int, CGFloat, Double, String, Bool, CGPoint, CGSize, CGRect, UIColor, UIImage
1
ответ дан 31 October 2019 в 13:21

Другие вопросы по тегам:

Похожие вопросы: