Search code examples
swiftinitiboutlet

How do I assign an IBOutlet a value using an Initializer?


I am having difficulty with something that I feel like should be working, I am wondering if there are some things that I'm not understanding about initializing values of IBOutlets.

So I've created a class that has three IBOutlets. I then created initializers for those outlet variables. When I create a new instance of the class and pass it hard-coded values I get nil in return.

So I've println()'ed the incoming settingLabel value to make sure incoming parameter indeed carries a value, it comes back as "hello" as to be expected. Then I attempt to assign the value to the settingsLabel.text, however when I call println(settingsLabel.text) comes back as nil.

enter image description here (Thread 1: EXV_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0))

My Question is:

What am I missing here and/or How do I assign an IBOutlet a value with an initializer?

This is my call to the initializer:

let cell = SettingCell(settingLabel: "hello", settingSwitch: true, timeSetting: 3)

This is my SettingCell.swift class:

class SettingCell: UITableViewCell {

    @IBOutlet weak var settingsLabel: UILabel!
    @IBOutlet weak var settingsSwitch: UISwitch!
    @IBOutlet weak var timeSetting: UILabel!

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
    }

    init(settingLabel: String, settingSwitch: Bool, timeSetting: NSNumber) {

        println(settingLabel) // returns hello <--------------------

        super.init(style: .Default, reuseIdentifier: "SwitchSettingCell")
        settingsLabel.text = settingLabel
        self.settingsSwitch.on = settingSwitch
        self.timeSetting.text = timeSetting.description as String

        println(settingsLabel.text) // returns nil <--------------------

    }

    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }


    var cellDelegate: SettingCellDelegate?


    @IBAction func handledSwitchChange(sender: UISwitch) {

        self.cellDelegate?.didChangeSwitchState(sender: self, isOn:settingsSwitch!.on)


    }

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

}

myStoryboard:

enter image description here


Solution

  • The IBOutlet keyword is just a way to tell Xcode that the view should be visible inside your storyboard. You can connect these outlets in your storyboard, and in doing so cause the variables to be instantiated & added as subviews from the storyboard instead of in code.

    In your code you are creating your cells programmatically, not from a storyboard (indeed if you tried to create them from a storyboard your app would crash, since you haven't implemented init(coder:)). Because of this, your variables have no reason to be "outlets" and are not ever initialised. You must initialise them yourself, add them as subviews & setup constraints, all in code.

    In your initialiser, your code is ineffectual because your variables are still nil. In fact, in your edit, you removed the optional chaining ?s which will have the effect of crashing your app, since the variables are force unwrapped, but are nil. This is the same reason println(settingsLabel.text) is crashing.


    Edit:

    Ok, so your issue is that you want to initialise your cells from the storyboard, but you're instead doing it programmatically by calling your custom initialiser. In tableView(_:cellForRowAtIndexPath:), you get your cells by dequeuing one from the reuse queue:

    let cell = tableView.dequeueReusableCellWithIdentifier("SettingsCell", forIndexPath: indexPath) as! SettingCell
    

    (This assumes you have given your cell prototype the identifier "SettingsCell" in the storyboard)

    Whenever a view is created from a storyboard, the view's init(coder:) initialiser is used, so you must provide an implementation of this in your SettingCell subclass. Once the cell has been dequeued, the cell's outlets will be initialised and added as subviews of the cell.

    So after dequeuing a cell, you can setup your labels and switch:

    cell.settingsLabel.text = //...
    cell.settingsSwitch.on = //...
    cell.timeSetting.text = //...