UIView 初始化覆盖导致 IBDesignable 崩溃

Posted

技术标签:

【中文标题】UIView 初始化覆盖导致 IBDesignable 崩溃【英文标题】:UIView init override causes IBDesignable to crash 【发布时间】:2015-11-06 14:55:11 【问题描述】:

我在使用 XCode 7.1 界面生成器时遇到了一个非常奇怪的问题。我有一个非常简单的 UIView 子类,在故事板编辑器中呈现良好

import UIKit

@IBDesignable
class DashboardHeaderView: UIView 

    @IBInspectable
    var maskClipHeight: CGFloat = 40.0

    override func layoutSubviews() 
        super.layoutSubviews()
        self.setMask()
    

    private func setMask() 
        let mask = CAShapeLayer()
        mask.path = self.createMaskPath()
        self.layer.mask = mask
    

    private func createMaskPath() -> CGPath 
        let maskPath = UIBezierPath()
        maskPath.moveToPoint(CGPoint(x: bounds.minX, y: bounds.minY))
        maskPath.addLineToPoint(CGPoint(x: bounds.maxX, y: bounds.minY))
        maskPath.addLineToPoint(CGPoint(x: bounds.maxX, y: bounds.maxY - self.maskClipHeight))
        maskPath.addLineToPoint(CGPoint(x: bounds.minX, y: bounds.maxY))
        maskPath.closePath()

        return maskPath.CGPath
    


但是,如果我只为其添加初始化程序覆盖:

required init?(coder aDecoder: NSCoder) 
    super.init(coder: aDecoder)

失败并出现错误:

错误:IB Designables:无法更新自动布局状态:代理崩溃 错误:IB Designables:无法呈现 DashboardHeaderView 实例:代理崩溃

我 100% 确定该初始化程序覆盖会使其崩溃,因为我已经复制了几次。如果我只注释掉它,它会再次起作用。

任何人都知道为什么会发生这种情况以及是否有办法修复/解决它?

【问题讨论】:

你试过不带参数的init()吗? @Losiowaty UIView 据我记得根本没有定义init()。而且,如果我理解得很好,界面构建器使用 init 和 coder 来初始化 View。 【参考方案1】:

我整天都在为此苦苦挣扎。你需要实现:

override init(frame: frame) 
    super.init(frame: frame)

这是 IBDesignable 代理用来实例化类的初始化程序。所以,就我而言,我还有另一个初始化程序。

init(frame: CGRect, maxValue: Double, minValue: Double) 
    super.init(frame: frame)

    self.maxValue = maxValue
    self.minValue = minValue

我的 init 阻止了 IBDesignable 需要的 init。一旦我如上所述覆盖了默认 init,我就可以选择保留我的 init 或将其转换为方便的 init:

convenience init(frame: CGRect, maxValue: Double, minValue: Double) 
    self.init(frame: frame)

    self.maxValue = maxValue
    self.minValue = minValue

现在我可以为 IBDesigner 添加一些默认行为:

var initForIB = false

init(frame: CGRect, maxValue: Double, minValue: Double) 
    super.init(frame: frame)

    self.maxValue = maxValue
    self.minValue = minValue
    initForIB = false


override init(frame: CGRect) 
    super.init(frame: frame)
    initForIB = true


required init?(coder aDecoder: NSCoder) 
    fatalError("init(coder:) has not been implemented")


override func drawRect(rect: CGRect) 
    if initForIB 
        initIBDefaults()
    
    // ...do some other stuff...

【讨论】:

【参考方案2】:

我遇到了类似的问题,发现在 prepareForInterfaceBuilder() 函数之外为 ibdesignable 视图设置掩码会导致渲染崩溃... prepareForInterfaceBuilder() 不从系统调用,仅由 interfaceBuilder 调用,因此您需要在此处和 awakeFromNib() 中设置 maskView。

【讨论】:

【参考方案3】:

我在另一个 SO 页面上找到了答案: @IBDesignable crashing agent

您需要同时覆盖init(frame:)init?(coder:)。如果只覆盖两者之一,IB 渲染会崩溃。

【讨论】:

以上是关于UIView 初始化覆盖导致 IBDesignable 崩溃的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Swift 1.0 中覆盖 UIView 初始化程序

在我的自定义 UIView 中,设置子视图的 backgroundColor 会导致它覆盖我的 drawRect 绘图(CGContextRef、CGMutablePathRef 等)。怎么修?

带有编码器的 UIView 初始化导致递归

在更改其嵌套的 UILabel 时移动 UIView 会导致视图跳回初始位置

UIView 添加/删除子视图与隐藏/显示的速度

UIView drawRect 仅在 UITableViewCell 中调用一次,导致重新使用单元格时数据过时