Подкласс Live Xcode Interface Builder UIView (IB_DESIGNABLE) без drawRect:

Можно ли создать подкласс UIView, который отображается в реальном времени в Xcode (путем добавления атрибута IB_DESIGNABLE, как объяснено здесь), но у него нет собственного метода drawRect:?

У нас есть пользовательский подкласс UIView, который использует некоторые CAShapeLayer, которые добавляются к self.layer для рисования (следовательно, нет необходимости переопределять drawRect:). Этот класс отлично работает в приложении, но не будет отображаться в Xcode.

Если мы воспроизведем код в drawRect:, он сработает, но мы бы предпочли, чтобы рисование происходило автоматически на слоях.

Можно ли это сделать?


Я также пытался сделать

- (void)drawRect:(CGRect)rect
{
    CGContextRef currentContext = UIGraphicsGetCurrentContext();
    CGContextMoveToPoint(currentContext, self.myLayer1.frame.origin.x, self.myLayer1.frame.origin.y);
    [self.myLayer1 renderInContext:currentContext];
    CGContextMoveToPoint(currentContext, self.myLayer1.frame.origin.x, self.myLayer1.frame.origin.y);
    [self.myLayer2 renderInContext:currentContext];    
}

который, кажется, работает на устройстве, но не на IB Xcode.


person Ricardo Sanchez-Saez    schedule 25.09.2014    source источник
comment
Убедитесь, что ваши коды инициализации (т. е. настройки CAShapeLayers) также вызываются из initWithFrame:. IB называет это предварительным просмотром.   -  person rintaro    schedule 25.09.2014
comment
Да, я тоже оттуда звоню. Неудачно. Откуда вы знаете, что IB использует initWithFrame:? Есть ли способ отладки кода представления, когда IB просматривает его?   -  person Ricardo Sanchez-Saez    schedule 25.09.2014
comment
EditorDebug Selected Views из меню Xcode.   -  person rintaro    schedule 25.09.2014
comment
Отладка выбранных представлений вызывает сбой Xcode с моим пользовательским представлением. :-(   -  person Ricardo Sanchez-Saez    schedule 25.09.2014
comment
Я отправил и сбой, и проблему с живым рендерингом CALayer в виде отчетов об ошибках в Apple.   -  person Ricardo Sanchez-Saez    schedule 27.10.2014


Ответы (3)


Вы можете просмотреть подклассы UIView в IB (с помощью макроса IB_DESIGNABLE) даже если drawRect: не переопределен.
Я добавил ваш code в XCode 6.1 и добавил OEProgressIndicator в файл xib. Затем я отладил его (используя меню Editor / Debug Selected View ), установив точку останова в вашем селекторе commonProgressIndicatorInit.

Вот почему вы ничего не видите в предварительном просмотре с вашим текущим кодом: когда вызывается commonProgressIndicatorInit (из конструктора initWithFrame:(CGRect)frame), frame равно CGRectZero (x:0 y:0 width:0 height:0 ), так что ваша центральная переменная фактически равна (0, 0), а радиус равен -1.
На устройстве, в зависимости от того, как используется класс, вы можете напрямую вызываться с соответствующим фреймом, поэтому он может работать на устройстве но не в ИБ.

Чтобы исправить это, я бы реализовал layoutSubviews селектор (переопределить его из UIView) для правильной организации подслоев. Этот селектор будет вызываться, когда frame изменится с CGRectZero на правильные значения, установленные в Interface Builder.

person yonel    schedule 30.01.2015

Я использовал метод - (void)prepareForInterfaceBuilder, чтобы сообщить IB, чтобы он отображал представление в реальном времени.

См. здесь: Создание интерактивного просмотра пользовательского объекта

Кроме того, вы, ребята, правы, что эта функция также доступна для Objective-C.

Вам не обязательно использовать drawRect, вы можете попробовать использовать - (void)layoutSubviews, это работает. Проблема с оставлением кода в таких местах, как - (void)layoutSubviews, только ради живого рендеринга заключается в том, что он может быть менее производительным и т. д. (например, вы можете делать много вещей в - (void)awakeFromNib, но этот метод не вызывается из живого рендеринга, поэтому просто убедитесь, что вы также выполнили все настройки в - (void)prepareForInterfaceBuilder.

person Edgar    schedule 25.10.2014
comment
Это не помогает отображать пользовательские CALayer, которые являются подслоями текущего представления. - person Ricardo Sanchez-Saez; 27.10.2014

Не видя всего вашего кода, трудно понять, в чем источник проблемы, но, чтобы ответить на ваш вопрос, да, вы можете использовать IBInspectable/IBDesignable без необходимости реализации какого-либо другого конкретного метода. Я сделал это для представления, которое использует много слоев и не рисует (для этого используются вложенные слои).

Фрагмент быстрого тестового примера со скругленными углами:

@IBDesignable
class MyView : UIView {

    @IBInspectable var cornerRadius:CGFloat {
        get { return self.layer.cornerRadius }
        set { self.layer.cornerRadius = newValue    }
    }

    @IBInspectable var borderWidth:CGFloat {
        get { return self.layer.borderWidth }
        set { self.layer.borderWidth = newValue }
    }

    @IBInspectable var borderColor:UIColor {
        get { return UIColor(CGColor: self.layer.borderColor) }
        set { self.layer.borderColor = newValue.CGColor }
    }
}

Простой пример с градиентами см. в этой публикации.

Объяснение того, как отлаживать просмотры в реальном времени, см. в WWDC §411 примерно на 22-й минуте.

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

person Chris Conover    schedule 07.10.2014
comment
Спасибо за ваш ответ, но этот класс, который мы сделали, вообще не работает. Если вам удастся отобразить его на Xcode 6, расскажите, как это сделать. См. две реализации обходного пути drawRect, которые мы пробовали, но безрезультатно. - person Ricardo Sanchez-Saez; 08.10.2014
comment
Насколько мне известно, это функция Swift. Начните со ссылки, которую я разместил в качестве основы, и отталкивайтесь от нее. - person Chris Conover; 09.10.2014
comment
IB_DESIGNABLE настраиваемые представления — это не только функция Swift. В документах четко указано, что он работает с Objective-C тоже. Хотя бы тот факт, что это не работает с пользовательскими дочерними CALayers, является ошибкой только для Objective-C. Я сообщил об этом в Apple. - person Ricardo Sanchez-Saez; 09.10.2014
comment
Ну, по крайней мере, у вас есть хороший обходной путь — использовать Swift. - person Chris Conover; 09.10.2014
comment
Использование Swift в нашей производственной среде пока не вариант. - person Ricardo Sanchez-Saez; 09.10.2014