简体   繁体   English

奥特莱斯是零?

[英]Outlets are nil?

I've created (and linked) some outlets to labels in my storyboard, and when I attempt to modify the text, my application throws an exception and freezes, because they are apparently nil . 我在故事板中为标签创建(并链接)了一些出口,当我尝试修改文本时,我的应用程序会抛出异常并冻结,因为它们显然是nil

Code: 码:

import UIKit

class QuoteView : UITableViewController {
    @IBOutlet var quoteCategory: UILabel!
    @IBOutlet var quoteTitle: UILabel!
    @IBOutlet var quoteAuthor: UILabel!
    @IBOutlet var quoteContent: UILabel!
    @IBOutlet var quoteTimestamp: UILabel!
}

I'm attempting to set them in the prepareForSegue method of previous view controller, like so: 我试图在前一个视图控制器的prepareForSegue方法中设置它们,如下所示:

overide func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 
    if (segue.identifier == "indivQuoteSegue") { 
         let svc = segue.destinationViewController as! QuoteView 
         let quote = quoteArray[(tableView.indexPathForSelectedRow?.row)!];
         svc.quoteTitle.text = quote.getQuoteTitle() 
         svc.quoteAuthor.text = quote.getQuoteAuthor()   
         svc.quoteContent.text = quote.getQuoteContent() 
         svc.quoteCategory.text = quote.getCategory() 
         svc.quoteTimestamp.text = quote.getQuoteTimestamp() 
    } 
} 

In prepareForSegue you outlets have not been initialized yet, so you can not set properties of them. prepareForSegue您的出口尚未初始化,因此您无法设置它们的属性。 You should create String properties and set those instead, or better yet just pass your quote object. 你应该创建String属性并设置它们,或者更好,只需传递你的quote对象。 For example: 例如:

overide func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 
if (segue.identifier == "indivQuoteSegue") { 
     let svc = segue.destinationViewController as! QuoteView 
     let quote = quoteArray[(tableView.indexPathForSelectedRow?.row)!];
     svc.quoteTitleString = quote.getQuoteTitle() 
     svc.quoteAuthorString = quote.getQuoteAuthor()   
     svc.quoteContentString = quote.getQuoteContent() 
     svc.quoteCategoryString = quote.getCategory() 
     svc.quoteTimestampString = quote.getQuoteTimestamp() 
} 
} 

Then in the viewDidLoad of QuoteView set your labels' text 然后在QuoteViewviewDidLoad中设置标签的文本

override func viewDidLoad(){
    self.quoteTitle.text = self.quoteTitleString
    etc...
}

Its happening because usually the UI(view) elements are not ready to be manipulated till viewDidLoad is called. 它的发生是因为通常UI(视图)元素在调用viewDidLoad之前还没有准备好进行操作。

A elegant solution here would be to create a "quote" property in QuoteView and set it in "prepareForSegue". 这里一个优雅的解决方案是在QuoteView中创建一个“quote”属性并将其设置为“prepareForSegue”。 Then later use that quote property to load the UI elements in viewDidLoad or viewWillAppear method. 然后使用该quote属性在viewDidLoad或viewWillAppear方法中加载UI元素。 see @Duncan C's answer for details. 有关详细信息,请参阅@Duncan C的答案。

overide func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 
if (segue.identifier == "indivQuoteSegue") { 
    let svc = segue.destinationViewController as! QuoteView 
    svc.quote = quoteArray[(tableView.indexPathForSelectedRow?.row)!];
  } 
} 

then In QuoteView 然后在QuoteView中

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated);
    self.quoteTitle.text = self.quote.getQuoteTitle() 
    self.quoteAuthor.text = self.quote.getQuoteAuthor()   
    self.quoteContent.text = self.quote.getQuoteContent() 
    self.quoteCategory.text = self.quote.getCategory() 
    self.quoteTimestamp.text = self.quote.getQuoteTimestamp()
}

Stepping back a little from the "why it doesn't work" question about trying to set a view controller's outlets from outside the view controller, I give you a flat rule: 稍微退一步“为什么它不起作用”的问题试图从视图控制器外部设置视图控制器的出口,我给你一个平坦的规则:

"Don't do that." “不要这样做。”

You should treat another view controllers outlets as private and not try to manipulate them. 您应该将另一个视图控制器出口视为私有,而不是试图操纵它们。 That violates the principle of encapsulation, and important idea in OO design. 这违反了封装原则,也是OO设计中的重要思想。

Imagine this: I have a control panel object for a piece of stereo equipment. 想象一下:我有一个立体声设备的控制面板对象。 The control panel has a bunch of control UI objects on it that let the user set equalization, noise reduction, etc. Then it has a public API that it uses to interface with the other software components of the system. 控制面板上有一堆控制UI对象,可以让用户设置均衡,降噪等。然后它有一个公共API,用于与系统的其他软件组件连接。

If other other objects reach into my control panel and do things like change the value of the equalization sliders, then I can't go back later and replace the UI elements for equalizers to use dials, or numeric inputs, or some other UI element, without breaking the rest of the app. 如果其他对象进入我的控制面板并执行诸如更改均衡滑块的值之类的操作,那么我以后就无法返回并将均衡器的UI元素替换为使用拨号,数字输入或其他一些UI元素,没有打破应用程序的其余部分。 I have tightly coupled other objects in my program to things that should be part of the private implementation of my control panel object. 我已将程序中的其他对象紧密耦合到应该是我的控制面板对象的私有实现的一部分。

In your case, it flat doesn't work because in prepareForSegue the destination view controller's views haven't yet been loaded. 在您的情况下,它是平面不起作用,因为在prepareForSegue中尚未加载目标视图控制器的视图。

Even in cases where it would work, you still should not do that. 即使在它可以工作的情况下,你仍然不应该这样做。

Instead, you should create public properties of your view controller that let an outside object specify values for settings. 相反,您应该创建视图控制器的公共属性,让外部对象指定设置的值。 Then the view controller's viewWillAppear method should apply the values in those properties to the UI. 然后视图控制器的viewWillAppear方法应该将这些属性中的值应用于UI。

When you do it that way, if you later make changes to the UI, all you have to do is to make sure the public properties still work as expected. 当您这样做时,如果您稍后对UI进行更改,您所要做的就是确保公共属性仍然按预期工作。

@beyowulf and @harshitgupta gave detailed examples of how to do it the right way. @beyowulf和@harshitgupta详细介绍了如何以正确的方式做到这一点。 (Although I would argue that the code to install the properties into the UI should in viewWillAppear, not viewDidLoad. That way, in cases where a view controller is shown, covered by another view controller that changes its values, then uncovered, it updates the changed values on-screen when it gets shown again.) (虽然我认为将属性安装到UI中的代码应该在viewWillAppear中,而不是viewDidLoad。这样,如果显示视图控制器,由另一个更改其值的视图控制器覆盖,然后被覆盖,它会更新再次显示时,屏幕上的值会更改。)

您不能在segue中分配它们,因为它们尚未初始化,您可能会考虑使用可选绑定但它也不会起作用,解决方案是将您的数据结构或类作为var传递给segued视图然后更新将它放在viewDidLoad()中的新ViewController中或使用didSet {}。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM