Swift/iOS:在加载视图控制器后IBOutlet为nil

9

我正在使用XCode 8.2.1构建一个应用程序,其中一些对象显示在2D板上,当用户点击其中一个对象时,应该显示一些有关它的信息,以样式化的模态信息框的形式。我的设计是将信息编写在单独的视图控制器中,在需要时显示。

我已经为第二个视图控制器设计了一个基本的存根,并在接口构建器中添加了一个标签。然后我将此标签与我的自定义VC类进行了ctrl链接:

class InfoViewController: UIViewController {
    @IBOutlet weak var info: UILabel!
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    func displayInfo() {
        info.attributedText = NSAttributedString(string: "abc")
    }
}

然而,当我测试我的应用并轻敲对象时,在我的自定义 VC 类的 viewDidLoad() 方法中,info 字段仍然是 nil。我显示 VC 的方式如下:

let infoViewController = InfoViewController()
infoViewController.modalPresentationStyle = .overCurrentContext
self.present(infoViewController, animated: true, completion: nil)
infoViewController.displayInfo()

(注意:最终我只会有一个单一的 InfoViewController 实例,但这仅用于测试。我不认为全局实例会有任何区别?)
正如我所说,无论是在 viewDidLoad() 方法内还是在 displayInfo() 方法内,info 都始终为 nil,因此设置其 attributedString 属性会导致应用程序崩溃。考虑到 present 方法可能会异步调用,我尝试从 viewDidLoad() 中调用 displayInfo(),但这没有任何区别。
有人能告诉我忘记了什么,可以使我的 IBOutlet 正确初始化吗?
谢谢!
David

你使用Storyboard吗?如果是的话,我假设你应该从Storyboard中获取InfoViewController,而不是创建一个新实例... - Ahmad F
3个回答

30

问题在于对InfoViewController()的引用,它独立于任何故事板场景实例化了视图控制器。你需要使用instantiateViewController

let infoViewController = storyboard?.instantiateViewController(withIdentifier: "Info") as! InfoViewController
infoViewController.modalPresentationStyle = .overCurrentContext
present(infoViewController, animated: true) {
    infoViewController.displayInfo()
}

几点说明:

  • 假设(a)您已经为故事板中的场景设置了“故事板ID”;(b)您已将该场景的基类设置为InfoViewController

  • 请注意,我在present的完成处理程序中调用了displayInfo,因为您可能不希望在场景呈现并连接插座之前调用它。


或者,您可以在实例化 InfoViewController之后立即更新其非插座属性,然后使其 viewDidLoad 接受这些属性并更新插座,例如:

class InfoViewController: UIViewController {
    var info: String!
    @IBOutlet weak var infoLabel: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
        infoLabel.attributedText = NSAttributedString(string: info)
    }
}
请注意,我将@IBOutlet的名称更改为infoLabel,并添加了名为infoString属性。通常情况下,outlets会带有指示控件类型的后缀,而像String属性这样的模型对象则没有后缀。(您只需要确保在连接检查器中删除旧的outlet,以避免因这些属性名称更改而出现问题。)
无论如何,您可以执行以下操作:
let infoViewController = storyboard?.instantiateViewController(withIdentifier: "Info") as! InfoViewController
infoViewController.info = "abc"
infoViewController.modalPresentationStyle = .overCurrentContext
present(infoViewController, animated: true, completion: nil)

关键是不要在实例化场景后立即尝试更新其插座,而要确保此操作延迟到调用viewDidLoad之后。


非常感谢!我以为调用 self.present 就会执行与故事板的必要链接。 - David

1
我替换了。
let vc = CCDetailViewController()

With

let vc = self.storyboard?.instantiateViewController(withIdentifier: "CCDetailViewController")

Finally

self.present(vc!, animated: true, completion: nil)

现在它可以工作了...

0
在我的情况下,我为同一类创建了一个新的视图控制器。这导致在故事板中出现了两个视图控制器,但是引用了同一类。删除旧的视图控制器后,一切正常运行。
希望对某人有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接