iosuitableviewstoryboardreactive-cocoa-3

Outlets linked to controls in a Static TableView are not initialized


I am trying to setup a master details navigation. I use storyboard, master is a dynamic table and details is a static table. storyboard I have a nameLabel setup as an outlet in the controller but when i try to access it in viewDidLoad, its still set to nil.

Instead of using prepareForSegue, I have used didSelectRowAtIndexPath which pushes the detail view like this: (because i'm using the TableViewBindingHelper, see https://github.com/ColinEberhardt/ReactiveTwitterSearch/tree/master/ReactiveTwitterSearch/Util)

    func showLessonView(lessonVM: LessonViewModel) {

        let lessonViewController = LessonViewController(WithViewModel: lessonVM)
        self.navigationController?.pushViewController(lessonViewController, animated: true)

    }

LessonViewController:

import Foundation
import ReactiveCocoa

class LessonViewController: UITableViewController {

@IBOutlet var lessonNameLabel: UILabel!

private var viewModel: LessonViewModel

init(WithViewModel viewModel: LessonViewModel){
    self.viewModel = viewModel
    super.init(nibName: nil, bundle: nil)
}

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

override func viewDidLoad() {
    super.viewDidLoad()
    bindData()
}

func bindData() {
    // null here!
    if (lessonNameLabel != nil) {
        lessonNameLabel.rac_text <~ viewModel.name
    }
}
}

How can I fix this?

Other sample code i have seen performs the navigation in segue which ends up calling the init(coder aDecoder: NSCoder) constructor and all the outlets are already initialized.


Solution

  • Because you initialise the view controller with the WithViewModel initialiser, it knows nothing about the storyboard and so the outlets are not hooked up. To get the outlets hooked up as specified in the storyboard, you need either to use a segue, or to use the storyboard's instantiateViewControllerWithIdentifier(identifier:) method to create the view controller. Either way, you can't (easily) pass the ViewModel as an argument for the initialisation, so you will need to expose the viewModel var (remove private) and set it separately in your showLessonView method. To use instantiateViewControllerWithIdentifier(identifier:), give your Lesson View Controller an identifier (say "LessonViewController") in the storyboard. Then amend your showLessonView as follows:

    func showLessonView(lessonVM: LessonViewModel) {
        let lessonViewController = self.storyboard!.instantiateViewControllerWithIdentifier(identifier:"LessonViewController") as! LessonViewController
        lessonViewController.viewModel = lessonVM
        self.navigationController?.pushViewController(lessonViewController, animated: true)
    }
    

    When a view controller is instantiated from a storyboard, the init(coder:) initialiser is used, so either remove the override of that method, or amend it to call the super implementation:

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }