Есть ли способ для Interface Builder отображать IBDesignable представления, которые не переопределяют drawRect:
Я очень редко переопределить drawRect в мой наследник UIView подклассы, как правило, предпочитая установить layer.contents с предварительным рендерингом изображений и часто с использованием нескольких подслоев или подвидов и манипулированием ими на основе входных параметров. Есть ли способ для IB отобразить эти более сложные стеки представлений?
8 ответов:
спасибо, @zisoft за то, что втянул меня в
prepareForInterfaceBuilder. Есть несколько нюансов с циклом рендеринга Interface Builder, которые были источником моих проблем и заслуживают внимания...
- подтверждается: вам не нужно использовать
-drawRect.установка изображений на UIButton состояния управления работает. Произвольные стеки слоев, похоже, работают, если иметь в виду несколько вещей...
- IB использует
initWithFrame:..не
initWithCoder.awakeFromNibтоже не назовешь.
init...вызывается только один раз за сеанс!--26-->т. е. один раз за повторную компиляцию всякий раз, когда вы вносите изменения в файл. При изменении свойств IBInspectable init больше не вызывается. Однако...
prepareForInterfaceBuilderвызывается при каждом изменении свойстваэто как иметь кво на всех ваших IBInspectables, а также другие встроенные свойства. Вы можете проверить это самостоятельно, имея свой
_setupметод вызывается, сначала только из вашегоinit..метод. Изменение IBInspectable не будет иметь никакого эффекта. Затем добавьте вызов также вprepareForInterfaceBuilder. Уахла! Обратите внимание, что ваш код времени выполнения, вероятно, потребуется некоторое дополнительное KVO, так как он не будет вызыватьprepareForIBметод. Подробнее об этом ниже...
init...слишком рано рисовать, установить содержание слоя, так далее.по крайней мере с моей
UIButtonподкласс, называя[self setImage:img forState:UIControlStateNormal]не имеет никакого эффекта В IB. Вам нужно позвонить изprepareForInterfaceBuilderили через крючок кво.
- когда IB не удается отобразить, он не очищает наш компонент your, а сохраняет последнюю успешную версию.
может быть запутанным время от времени, когда вы делаете изменения, которые не имеют никакого эффекта. Проверьте сборку системный журнал.
совет: держите Монитор активности рядом
я все время зависаю на нескольких разных процессах поддержки, и они берут с собой всю машину. ПрименитьForce Quitобильно.(обновление: это на самом деле не было правдой, так как XCode6 вышел из бета-версии. Он редко висит больше)
обновление
- 6.3.1 кажется, не нравится KVO в версии IB. Теперь вам, похоже, нужен флаг, чтобы поймать Interface Builder и не настраивать 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
этот ответ связан с переопределением 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:
вам не нужно использовать drawRect, вместо этого вы можете создать свой пользовательский интерфейс в файле xib, загрузить его в initWithCoder и initWithFrame, и это будет живой рендеринг в IB после добавления IBDesignable. Проверьте этот краткий учебник:https://www.youtube.com/watch?v=L97MdpaF3Xg
Я думаю, 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 } }Я не тестировал этот фрагмент, но раньше использовал такие шаблоны. Обратите внимание на использование свойства lazy, делегирующего вычисляемому свойству, для упрощения начальной конфигурации.
уточнение ответ Хари Карам Сингха, это слайд-шоу объясняет далее:
http://www.splinter.com.au/presentations/ibdesignable/
затем, если вы не видите, что ваши изменения отображаются в Interface Builder, попробуйте следующие меню:
- Xcode - > Редактор - > Автоматическое Обновление Представлений
- Xcode - > Редактор - > Обновить Все Представления
- Xcode - > Редактор - > Выбрана Отладка Просмотров
к сожалению, отладка моего представления заморозила Xcode, но она должна работать для небольших проектов (YMMV).
в моем случае было две проблемы:
Я не реализовал
initWithFrameв пользовательский вид: (обычноinitWithCoder:вызывается при инициализации через IB, но по какой-то причинеinitWithFrame:нуженIBDesignableтолько. Не вызывается во время выполнения при реализации через IB)перо моего пользовательского представления загружалось из
mainBundle:.
Я считаю, что вы можете реализовать
prepareForInterfaceBuilderи сделайте свою основную анимацию там, чтобы она появилась в IB. Я сделал некоторые причудливые вещи с подклассами UIButton, которые выполняют свою собственную работу с основным анимационным слоем для рисования границ или фонов, и они живут в interface builder просто отлично, поэтому я думаю, что если вы напрямую подклассируете UIView, тоprepareForInterfaceBuilderвсе, что вам нужно сделать по-другому. Имейте в виду, однако, что метод выполняется только когда-либо ИБотредактировано, чтобы включить код по запросу
у меня есть что-то похожее, но не совсем так (извините, я не могу дать вам то, что я действительно делаю, но это работа)
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чтобы сделать ИБ счастливым.затем в
commonInitЯ настроил основную анимацию, чтобы нарисовать граница и сделать его красивым.Я также реализую
willSetдля выделенной переменной, чтобы изменить цвет фона, потому что я ненавижу, когда кнопки рисуют границы, но не обеспечивают обратную связь при нажатии (я ненавижу, когда нажатая кнопка выглядит как несжатая кнопка)
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
