Я очень редко переопределяю drawRect в своих подклассах UIView, обычно предпочитая устанавливать layer.contents
с предварительным рендерингом изображений и часто использованием нескольких подуровней или подпредставлений и управления ими на основе входных параметров. Существует ли путь к IB для рендеринга этих более сложных стопок представления?
Спасибо, @zisoft для clueing меня в на prepareForInterfaceBuilder
. Там несколько нюансов с циклом рендеринга Интерфейсного Разработчика, которые были источником моих проблем и стоит отметить...
-drawRect
. Установка отображает на работах состояний управления UIButton. Произвольные стопки слоя, кажется, работают, если несколько вещей имеются в виду...
initWithFrame:
.. не initWithCoder
. awakeFromNib
также НЕ назван.
init...
только назван однажды на сессию Т.е. после того как на перекомпилировали каждый раз, когда Вы вносите изменение в файле. При изменении свойств IBInspectable init НЕ называют снова. Однако...
prepareForInterfaceBuilder
назван на каждом изменении свойства , Оно похоже на наличие KVO на всем Вашем IBInspectables, а также других встроенных свойствах. Можно протестировать это сами при наличии Вашего _setup
названный метод, сначала только от Вашего init..
метод. Изменение IBInspectable не будет иметь никакого эффекта. Затем добавьте вызов также к [1 110]. Whahla! Отметьте, для Вашего кода во время выполнения будет, вероятно, нужен некоторый дополнительный KVO, так как он не будет звонить prepareForIB
метод. Больше на этом ниже...
init...
слишком рано тянуть, устанавливать содержание слоя, и т.д. , По крайней мере, с моим UIButton
, подкласс, звоня [self setImage:img forState:UIControlStateNormal]
не имеет никакого эффекта в IB. Необходимо назвать его от [1 115] или через рычаг KVO.
Может сбивать с толку время от времени, когда Вы вносите изменения, которые не имеют никакого эффекта. Проверьте журналы сборки.
<забастовка> , я добираюсь, зависает все время на паре различных процессов поддержки, и они удаляют целую машину с ними. Подайте заявку Force Quit
подробно. забастовка>
(ОБНОВЛЕНИЕ: Это действительно не было верно, так как XCode6 вышел из беты. Это редко зависает больше)
, ОБНОВЛЕНИЮ
prepareForInterfaceBuilder
метод эффективно KVOs весь эти IBInspectable
свойства. Неудачно, что это поведение не зеркально отражается так или иначе во времени выполнения, таким образом требующем ручного KVO. См. обновленный пример кода ниже. Ниже является некоторый пример кода рабочего подкласса 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
}
}
я не протестировал этот отрывок, но использовал шаблоны как это прежде. Отметьте использование ленивого делегирования свойства к вычисленному свойству для упрощения первоначальной конфигурации.
В моем случае было две проблемы:
я не реализовал initWithFrame
в пользовательском представлении: (Обычно initWithCoder:
назван, когда Вы инициализируете через IB, но по некоторым причинам initWithFrame:
, необходим для IBDesignable
только. Не назван во время времени выполнения, когда Вы реализуете через IB)
, перо Моего пользовательского представления загружалось от mainBundle
: [NSBundle bundleForClass:[self class]]
был необходим.
Для разработки ответ Karam Singh Хари этот слайд-шоу объясняет далее:
http://www.splinter.com.au/presentations/ibdesignable/
Затем, если Вы не видите, что Ваши изменения обнаруживаются в Интерфейсном Разработчике, попробуйте эти меню:
, К сожалению, отлаживая мое представление заморозили XCode, но это должно работать на маленькие проекты (YMMV).
Я полагаю, что можно реализовать 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
, чтобы выделенная переменная изменила цвет фона, потому что я ненавижу, когда кнопки тянут границы, но не обеспечивают обратную связь при нажатии (я ненавижу его, когда нажатая кнопка похожа на ненажатую кнопку)
макрос 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