drawRect 在 Xcode 游乐场抛出“致命错误:在展开可选值时意外发现 nil”

Posted

技术标签:

【中文标题】drawRect 在 Xcode 游乐场抛出“致命错误:在展开可选值时意外发现 nil”【英文标题】:drawRect throws "fatal error: unexpectedly found nil while unwrapping an Optional value" in Xcode playground 【发布时间】:2016-05-11 16:17:26 【问题描述】:

为什么下面的代码在 Xcode 游乐场运行时会抛出 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

【问题讨论】:

layersetup() 中来自哪里? 附言。这就是为什么你应该避免隐式展开的选项。 【参考方案1】:

您不应使用 init(layer:) 初始化程序来创建您的 CAShapeLayers。 From 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)

这正是initUIView 实现所做的。

【讨论】:

以上是关于drawRect 在 Xcode 游乐场抛出“致命错误:在展开可选值时意外发现 nil”的主要内容,如果未能解决你的问题,请参考以下文章

在 Xcode 11 游乐场中使用 SwiftUI

评论在 Xcode 7 游乐场未显示是标记格式

Xcode:有啥方法可以刷新/重新运行游乐场?

在 xcode 中将资源添加到游乐场

在独立的 Xcode 游乐场中运行 c 或 c++ 代码 [关闭]

Xcode 8.3.2 游乐场实时视图渲染不起作用