Live Xcode 的 Interface Builder UIView 子类 (IB_DESIGNABLE) 没有 drawRect:

Posted

技术标签:

【中文标题】Live Xcode 的 Interface Builder UIView 子类 (IB_DESIGNABLE) 没有 drawRect:【英文标题】:Live Xcode's Interface Builder UIView subclass (IB_DESIGNABLE) without drawRect: 【发布时间】:2014-09-25 14:55:21 【问题描述】:

是否可以创建一个在 Xcode 中实时呈现的UIView 子类(通过添加here 解释的IB_DESIGNABLE 属性)但没有自定义drawRect: 方法?

我们有一个自定义的UIView 子类,它使用一些CAShapeLayers 添加到self.layer 进行绘图(因此,无需覆盖drawRect:)。此类在 App 上运行良好,但不会在 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];    

这似乎适用于设备,但不适用于 Xcode 的 IB。

【问题讨论】:

确保您的初始化(即设置CAShapeLayers)代码也从initWithFrame: 调用。 IB 称之为预览。 是的,我也从那里调用它。没运气。你怎么知道IB使用initWithFrame:? IB 预览时是否有调试视图代码的方法? Editor > Debug Selected Views 来自 Xcode 菜单。 Debug Selected Views 使 Xcode 在我的自定义视图中崩溃。 :-( 我将崩溃和 CALayer 实时渲染问题作为错误报告提交给 Apple。 【参考方案1】:

您可以在 IB 中预览 UIView 子类(使用 IB_DESIGNABLE 宏)即使 drawRect: 未被覆盖。 我在 XCode 6.1 中添加了您的 code 并将 OEProgressIndicator 添加到 xib 文件中。然后我通过在 commonProgressIndicatorInit 选择器中设置断点来调试它(使用菜单 Editor / Debug Selected View )。

这就是为什么您在当前代码的预览中看不到任何内容的原因当调用 commonProgressIndicatorInit(来自 initWithFrame:(CGRect)frame 构造函数)时,frame 是等于CGRectZero (x:0 y:0 width:0 height:0),这样您的中心变量实际上等于(0, 0),半径为-1。 在设备上,根据类的使用方式,您可能会被正确的帧直接调用,这就是为什么它可以在设备上工作但不能在 IB 中工作的原因。

为了解决这个问题,我将实现 layoutSubviews 选择器(从 UIView 覆盖它)来正确组织子层。当 frame 将从 CGRectZero 更改为 Interface Builder 中设置的正确值时,将调用此选择器。

【讨论】:

【参考方案2】:

我一直在使用 - (void)prepareForInterfaceBuilder 方法来告诉 IB 实时渲染视图。

请看这里:Creating a Live View of a Custom Object

另外,你们是对的,这个功能也适用于 Objective-C。

你不一定要用drawRect,你可以试试- (void)layoutSubviews,好像行得通。将代码留在- (void)layoutSubviews 之类的地方只是为了实时渲染的问题是它的性能可能会降低等(例如,您可以在- (void)awakeFromNib 中做很多事情,但不会从实时渲染,所以请确保您也在 - (void)prepareForInterfaceBuilder 中进行了所有设置。

【讨论】:

这无助于渲染作为当前视图子层的自定义 CALayers。【参考方案3】:

如果不查看所有代码,很难看出问题的根源是什么,但要回答您的问题,是的,您可以使用 IBInspectable / IBDesignable 而无需实现任何其他特定方法。我已经为使用许多图层且不进行任何绘图的视图执行此操作(为此使用嵌套图层)。

对于一个带圆角的快速测试示例 sn-p:

@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 
    

有关渐变的简单示例,请参阅this post。

有关如何调试实时视图的说明,请参阅 WWDC §411 大约 22 分钟。

到目前为止,我看到的唯一限制是您可以在类扩展中添加可检查的属性,但它们只能在设备上正确呈现。

【讨论】:

感谢您的回答,但我们制作的this class 根本不起作用。如果你设法让它在 Xcode 6 上显示,请告诉我如何。请参阅我们尝试的两个 drawRect 解决方法实现,但无济于事。 据我所知,这是 Swift 独有的功能。从我作为基线发布的链接开始,然后从那里分支。 IB_DESIGNABLE 自定义视图不是 Swift 独有的功能。 docs 明确指出它也适用于 Objective-C。如果只是,这不适用于自定义子 CALayers 的事实是 Objective-C only 错误。我reported it苹果。 啊,至少你有一个很好的解决方法——使用 Swift。 在我们的生产环境中使用 Swift 暂时不是一个选项。

以上是关于Live Xcode 的 Interface Builder UIView 子类 (IB_DESIGNABLE) 没有 drawRect:的主要内容,如果未能解决你的问题,请参考以下文章

Interface Builder文件中的Xcode 6未知类

Xcode 和 Interface Builder 随机停止对话

Xcode 4.2 Interface Builder Dock 图标

Xcode 8 属性“隐藏”不会在 Interface Builder 中隐藏视图

编辑情节提要时 Xcode Interface Builder 崩溃

如何防止 Xcode/Interface Builder 自动向下移动视图?