drawRect抛出"致命错误:在解开一个Optional值时意外发现nil"在Xcode游乐场

时间:2016-05-11 16:17:27

标签: swift swift-playground xcplayground

为什么以下代码在Xcode playground中运行时会抛出fatal error: unexpectedly found nil while unwrapping an Optional value?我不确定以下代码有什么问题。谢谢你的帮助。我还没试过在游乐场外跑步。

import UIKit
import XCPlayground

class CircularProgressView: UIView {
    var progressBackgroundLayer: CAShapeLayer!
    var progressLayer: CAShapeLayer!
    var iconLayer: CAShapeLayer!

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.setup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.setup()
    }

    convenience init() {
        self.init(frame: CGRectZero)
    }

    func setup() {
        progressBackgroundLayer = CAShapeLayer(layer: layer)
        progressLayer = CAShapeLayer(layer: layer)
        iconLayer = CAShapeLayer(layer: layer)
    }

    override func drawRect(rect: CGRect) {
        progressBackgroundLayer.frame = self.bounds
        progressLayer.frame = self.bounds
        iconLayer.frame = self.bounds
    }
}


var progressView = CircularProgressView(frame: CGRectMake(0, 0, 80, 80))

XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
XCPlaygroundPage.currentPage.liveView = progressView

1 个答案:

答案 0 :(得分:2)

您不应使用init(layer:)初始化程序来创建CAShapeLayersFrom the documentation(强调我的):

  

此初始值设定项用于创建图层的阴影副本,例如,用于presentationLayer方法。 在任何其他情况下使用此方法将产生未定义的行为。例如,不要使用此方法初始化具有现有图层内容的新图层。

因此,由于在这种情况下调用此初始化器的行为未定义,它会在您的情况下返回nil - 然后您在访问时强制解包,因为它是一个隐式解包的可选项。

您应该只使用init()创建图层。我还建议您内联定义图层实例,并删除隐式展开的选项,如they're inherently unsafe。例如:

class CircularProgressView: UIView {
    let progressBackgroundLayer = CAShapeLayer()
    let progressLayer = CAShapeLayer()
    let iconLayer = CAShapeLayer()

...

要将图层添加到视图中,您需要在视图图层上使用addSublayer:方法。例如:

func setup() {
    layer.addSublayer(progressBackgroundLayer)
    layer.addSublayer(progressLayer)
    layer.addSublayer(iconLayer)
}

此外,drawRect:用于绘制视图的内容,因此完全是定义图层框架的错误位置(只应在视图边界发生变化时才会出现)。您应该考虑在layoutSubviews中执行此操作。

override func layoutSubviews() {
    progressBackgroundLayer.frame = bounds
    progressLayer.frame = bounds
    iconLayer.frame = bounds
}

最后,这段代码毫无意义:

convenience init() {
    self.init(frame: CGRectZero)
}

这正是UIViewinit实施的原因。