了解错误"Unexpectedly found nil while unwrapping an optional value"



我是编码/Swift 的新手,我知道这个问题问了很多。我搜索并阅读,但我不完全理解它。

我将 UIImageView 连接到我的视图控制器:

@IBOutlet var bi01: UIImageView!

我通过Xcode界面设置了一个图像,但以防万一我也通过代码设置了相同的图像:

override func viewDidLoad() {
super.viewDidLoad()
bi01.image = //image
}

所以据我所知,bi01在这一点上不是零吗?

我用了一个按钮来更改bi01的图像:

@IBAction func bb01(_ sender: UIButton) {
updatebi01()
}

并为它创建了一个数组:

let oneToTwo = [image1, image2, image3]

所以如果我在视图控制器中编写,这个函数有效.swift:

func updatebi01() {
bi01.animationImages = oneToTwo
bi01.animationDuration = 0.3
bi01.animationRepeatCount = 1
bi01.startAnimating()
}

到目前为止没有问题。但是我想对许多图片使用相同的函数,所以我创建了一个新的 swift 文件并将这个函数移到那里,我还为 viewcontroller 创建了一个实例:

import Foundation
var screenInstance = ViewController()
func updatebi01() {
screenInstance.bi01.animationImages = oneToTwo
screenInstance.bi01.animationDuration = 0.3
screenInstance.bi01.animationRepeatCount = 1
screenInstance.bi01.startAnimating()
}

在此之后,当我尝试该按钮时,xcode 在此行中给了我这个著名的错误:

screenInstance.bi01.animationImages = oneToTwo

现在,我可以在视图控制器中使用此代码.swift,我可以通过忽略我的第二个 attemp/way 来处理这个问题。但我不明白问题本身。

我只是想学习/理解这个,它是相同的代码。它可以在视图控制器内部工作.swift但在视图控制器之外不起作用。

您面临的问题不是因为您在视图控制器之外执行此操作,而是因为您如何实例化视图控制器以及可能调用updatebi01()的时间。这不是强制展开的问题,而是视图控制器管道的问题。

首先,如果你有渠道@IBOutlet我希望你正在使用故事板。若要从情节提要实例化视图控制器,您需要在情节提要中设置标识符,然后像这样使用它:

var screenInstance = UIStoryboard(name: "fileNameHereWithoutExtension", bundle: nil).instantiateViewController(withIdentifier: "idYouSetUpInStoryboard") as! ViewController

所以一个非常快速的解释:通过像ViewController()那样调用默认构造函数,不会真正添加任何字段。ViewController实例可能具有完全不同的设计,并且可以由多个情节提要或标识符实例化。所以你所做的只是创建了一个类。在这种情况下,您可以执行以下操作:

var screenInstance: ViewController = {
let controller = ViewController()
controller.bi01 = {
let view = UIImageView()
view.frame = CGRect.zero
controller.view.addSubview(view)
return view
}()
return controller    
}() 

现在这是有道理的。由于您是以编程方式执行此操作的,因此您还需要满足类的所有参数。由于您正在强制解开包装bi01因此您是在说"无论谁在实例化此类的实例,都需要确保始终设置bi01"。所以设置它。

我希望这能清除有关以编程方式执行此操作的部分。但是,现在让我们假设您正在使用故事板。让我们看一下下面的代码:

let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewController") as! ViewController
viewController.bi01.animationImages = oneToTwo

现在视图控制器已正确实例化,如果bi01nil则情节提要中存在问题。但是有一个技巧:bi01实际上将在调用viewDidLoad之前被实例化,因此在大多数情况下,它将在创建后立即nil。您需要等待视图控制器实际加载。

因此,不要访问视图控制器外部的视图。您需要的是:

class ViewController: UIViewController {
@IBOutlet private var bi01: UIImageView! // Note private
var animationData: (images: [UIImage], duration: TimeInterval, repeatCount: Int)!
override func viewDidLoad() {
super.viewDidLoad()
bi01.animationImages = animationData.images
bi01.animationDuration = animationData.duration
bi01.animationRepeatCount = animationData.repeatCount
bi01.startAnimating()
}
}

现在你打电话:

let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewController") as! ViewController
viewController.animationData = (oneToTwo, 0.3, 1)

您的视图控制器将在需要时完成其余的工作。

为了解释最后一部分,viewDidLoad将在二传手之后调用animationData。实际上,在窗口层次结构中添加控制器时会调用视图确实加载。所以最常见的是做:

let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewController") as! ViewController
viewController.animationData = (oneToTwo, 0.3, 1)
present(viewController, animated: true, completion: nil)

您还可以争辩说,您可以通过执行以下操作来访问视图:

let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewController") as! ViewController
present(viewController, animated: true) {
viewController.bi01... // This will work
}

但问题是块是在视图呈现后调用的,即在动画之后。这意味着用户将首先看到默认值,当动画结束时,视图将突然更改以反映您正在设置的数据。另一方面,视图确实加载将在动画开始之前调用。

还有更多内容,但我希望这能为您解决一些问题。

好的,所以当你创建一个变量var screenInstance = ViewController(),你只创建一个ViewController类型的变量。 此变量没有当前在内存中的视图控制器的引用。请记住,在 iOS Swift 中,当您实例化/呈现或推送 viewController 时,会在内存中创建一个新实例。为了在类之外访问视图控制器,您必须将其引用传递给其变量。

class animationImage: NSObject {
func updatebi01(viewController : UIViewController) {
let screenInstance = viewController as! ViewController // or your viewController
screenInstance.bi01.animationImages = screenInstance.oneToTwo
screenInstance.bi01.animationDuration = 0.3
screenInstance.bi01.animationRepeatCount = 1
screenInstance.bi01.startAnimating()
}
}

你可以从你的视图控制器调用这个方法,就像。

animationImage.updatebi01(viewController : self)

您还必须更改此函数的参数,并使用 viewController 的参考发送图像。

所以如果你没有得到这个,请告诉我。

更好的方法是将UIViewController作为输入参数,如下所示

func updatebi01(screenInstance: UIViewController) {
screenInstance.bi01.animationImages = oneToTwo
screenInstance.bi01.animationDuration = 0.3
screenInstance.bi01.animationRepeatCount = 1
screenInstance.bi01.startAnimating()
}

相关内容

最新更新