大约一个月前,我开始学习swift(主要来自书籍),没有任何先前的编程经验。我仍然无法绕过几个概念,主要是值类型和引用类型,以及委托等事情。这可能就是为什么我似乎无法找到以下问题的答案。
我有一个包含2个类的Xcode项目,如2个swift文件。
在viewController类中,我有一个方法来禁用和启用collectionView的用户交互:
func collectionViewInteractionEnabled(boolean: Bool) {
collectionView.userInteractionEnabled = boolean
}
从viewController类调用时,该方法有效。但是当从collectionViewCell类调用时,我得到了这个:
“致命错误:在解开可选值时意外发现nil”
Xcode强调我的功能内容是罪魁祸首。这让我觉得collectionView不适用于collectionViewCell类。但是我的婴儿编程大脑实际上无法遵循解决这个问题所需的思维过程。
我找到的唯一解决方案是使用通知让函数执行。它有效,但是我不应该知道另一种方式吗?是否可以从另一个类调用一个函数,并且仍然可以执行它,就好像从声明函数的类中调用它一样?
编辑:尝试Patricks建议后更新:
问号会阻止应用程序崩溃,但不会更改代码的行为。确实在故事板中创建了collectionView
,其中连接了3个出口,引用,以及数据源和委托。
调用该函数并在viewController
类中正确执行。所以我认为它不是零。如果我尝试从collectionViewCell
类(一个不同的.swift文件)调用它,那么它通常是零。我以为collectionViewCell' class just can't see it since the
collectionView`是在其他地方创建的?这对我来说没有意义,我想的越多,我就越迷失。
可能是因为我只是从另一个文件调用函数错误了吗?我正在使用:
在collectionViewCell类中
ViewController().collectionViewInteractionEnabled(true)
答案 0 :(得分:2)
我假设您的collectionView
属性为@IBOutlet
,其类型为UICollectionView!
,这是一种隐式展开的类型(由!
指示)。这意味着此值可能为nil,也可能不为nil,如果您尝试在nil时直接访问它,您的应用程序将崩溃并显示您收到的确切错误消息。如果它们不是零,有几种方法可以“解开”这些值,以便安全地使用这些值。在你的情况下,我会这样做:
collectionView?.userInteractionEnabled = boolean
这称为Optional Chaining。如果此行执行时collectionView
为nil,则执行将中止并移至下一行。但是,如果将collectionView
定义为非零值,则userInteractionEnabled
属性将设置为boolean
的值。当然,现在,为什么你的collectionView
为零?如果这是@IBOutlet
,您可能没有连接Interface Builder中的引用插座。如果您以编程方式创建集合视图,那么您在定义它之前尝试访问它。此外,如果在viewDidLoad()
方法调用拥有集合视图的视图控制器之前未调用此函数,则在任何一种情况下仍可能会出现此错误。此时,您的视图已加载,您的collectionView
值将被定义,您可以安全地与其进行互动。
答案 1 :(得分:0)
查看代码后,问题出在此处:
MemoryController().memoryCollectionViewUserInteractionEnabled(true)
当您致电MemoryController()
时,您正在创建该类的新实例,而不是对我从您认为您期望的故事板中加载的实例的引用。 instantiating a view controller from a storyboard有一个特殊程序。如果您在没有故事板的情况下实例化,那么您的视图将不会被定义并链接为@IBOutlet
引用,并且将为零,因此崩溃。
但那不是你真正想要的 - 你想要一个从故事板加载的引用。一种简单的方法是在单元格和视图控制器之间建立委托关系。让您的视图控制器实现协议,并让您的单元格使用键入该协议的属性,然后视图控制器可以在创建单元格时将其设置为self
。
// Define the protocol
protocol MemoryCardCellDelegate {
func cardDidTurnDown()
}
class MemoryCardCell: UICollectionViewCell, UICollectionViewDelegate {
// Create property typed to the protocol
var delegate: MemoryCardCellDelegate?
func turnDownAfterTimer() {
// Call the method on your delegate (which is really your view controller)
self.delegate?.cardDidTurnDown()
}
}
// Add protocol conformance to type declaratio
class MemoryController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, MemoryCardCellDelegate {
// Implement the protocol method
func cardDidTurnDown() {
memoryCollectionViewUserInteractionEnabled(true)
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
if let cell = memoryCollectionView.dequeueReusableCellWithReuseIdentifier("MemoryCardCell", forIndexPath: indexPath) as? MemoryCardCell {
let image = UIImage(named: "Mem Card Back")
cell.cardImageView.image = image
cell.delegate = self //< Set the delegate
return cell
}
fatalError( "Could not dqueue MemoryCardCell." )
}
}