Interface Builder是否有一种方法可以呈现不覆盖drawRect的IBDesignable视图:

Har*_*ngh 63 xcode objective-c interface-builder swift

我很少在我的UIView子类中覆盖drawRect,通常更喜欢layer.contents使用预渲染图像进行设置,并且经常使用多个子图层或子视图,并根据输入参数对这些图层进行操作.有没有办法让IB渲染这些更复杂的视图堆栈?

Har*_*ngh 133

谢谢,@ zisoft为我提供了线索prepareForInterfaceBuilder.Interface Builder的渲染周期有一些细微差别,这是我的问题的根源,值得注意......

  1. 确认:您不需要使用-drawRect.

在UIButton控制状态下设置图像有效.如果记住一些事情,任意层叠似乎都有用......

  1. IB使用 initWithFrame:

..不initWithCoder. awakeFromNib也没有被调用.

  1. init... 每个会话只调用一次

即每当你在文件中进行更改时,每次重新编译一次.更改IBInspectable属性时,不会再次调用init.然而...

  1. prepareForInterfaceBuilder 每次改变财产都要求

就像在所有IBInspectables以及其他内置属性上使用KVO一样.您可以通过_setup调用您的init..方法来自行测试,首先只能从您的方法中进行测试.更改IBInspectable将不起作用.然后添加呼叫 prepareForInterfaceBuilder.Whahla!注意,您的运行时代码可能需要一些额外的KVO,因为它不会调用该prepareForIB方法.更多关于此...

  1. init... 现在绘制太快,设置图层内容等等.

至少在我的UIButton子类中,调用[self setImage:img forState:UIControlStateNormal]对IB没有影响.你需要从prepareForInterfaceBuilderKVO钩子或通过KVO钩子调用它.

  1. 当IB无法渲染时,它不会使我们的组件空白,而是保留最后一个成功的版本.

当您进行无效的更改时,可能会感到困惑.检查构建日志.

  1. 提示:将Activity Monitor放在附近

我总是挂起几个不同的支持流程,他们把整个机器都拿下来.Force Quit自由地申请.

(更新:自从XCode6退出测试版以来,情况并非真的如此.它很少再挂起)

UPDATE

  1. 6.3.1似乎不喜欢IB版本中的KVO.现在您似乎需要一个标志来捕获Interface Builder而不是设置KVO.这是好的,因为该prepareForInterfaceBuilder方法有效地KVO所有IBInspectable属性.不幸的是,这种行为在运行时没有以某种方式镜像,因此需要手动KVO.请参阅下面的更新示例代码.

UIButton子类示例

下面是一个有效的IBDesignable UIButton子类的示例代码.~~ 注意,prepareForInterfaceBuilder实际上不作为志愿监听改变我们的相关属性,并触发重绘需要.~~更新:见上文第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
Run Code Online (Sandbox Code Playgroud)

  • 很好的答案 - 谢谢!这是我在这个主题上看到的最好的写作. (3认同)

zis*_*oft 15

这个答案与覆盖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
            ...
        }

    }

}
Run Code Online (Sandbox Code Playgroud)

这是它在InterfaceBuilder中的外观:

在此输入图像描述

  • 显然有一个编译器标志:`#if!TARGET_INTERFACE_BUILDER` (8认同)

Les*_*ary 9

您不必使用drawRect,而是可以在xib文件中创建自定义接口,在initWithCoder和initWithFrame中加载它,并在添加IBDesignable后在IB中进行实时渲染.查看这个简短的教程:https://www.youtube.com/watch?v = L97MdpaF3Xg


Chr*_*ver 7

我认为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
    }
}
Run Code Online (Sandbox Code Playgroud)

我没有测试过这个片段,但之前使用过这样的模式.请注意,使用委托给计算属性的惰性属性来简化初始配置.


Zac*_*ris 5

为了详细说明Hari Karam Singh 的回答,这张幻灯片进一步解释了:

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

然后,如果您没有在 Interface Builder 中看到您的更改,请尝试以下菜单:

  • Xcode->编辑器->自动刷新视图
  • Xcode->编辑器->刷新所有视图
  • Xcode->Editor->Debug Selected Views

不幸的是,调试我的视图冻结了 Xcode,但它应该适用于小型项目 (YMMV)。