1

I have a "Country" ViewController with two ContainerViews: Side Menu (TocVC) and Data Screen (CountryDetailsVC) on it. Button that controls Side Menu toggling is located in Navigation bar.

Side Menu has a TableView and upon didSelectRowAt indexPath I would like pass its String value back to Data Screen and assign it to a Label on it.

This is what I've tried:

TocVC:

var selectedItem:String = ""
...

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        selectedItem = contents[indexPath.row]
        let VC = CountryDetailsVC()
        VC.labelUpdate(dataFrom: selectedItem)
    }

CountryDetailsVC:

@IBOutlet weak var label: UILabel!
...

func labelUpdate(dataFrom: String) {
        self.label.text = dataFrom
    }

But I'm getting Fatal error: Unexpectedly found nil while unwrapping an Optional value as I understand because TocVC passed data but CountryDetailsVC didn't "refresh" itself and run the function.

What is the right way to do what I'm trying to do?

EDIT: Storyboard screenshot Storyboard

EDIT1: Updated code.

Country:

let detailsVc = CountryDetailsVC()

TocVC:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        selectedItem = contents[indexPath.row]

        Country().detailsVc.labelUpdate(dataFrom: selectedItem)

CountryDetailsVC:

@IBOutlet weak var label: UILabel!

    func labelUpdate(dataFrom: String) {
        print("dataFrom: \(dataFrom)")
        self.label.text = dataFrom
    }

The function prints out dataFrom string to the console but crashes on the next line trying to assign dataFrom to label.text. When I change label: UILabel! to label: UILabel? it doesn't crash but not changing text either. Maybe it has something to do with this similar situation?

EDIT2: Updated code. Reference to self in segue to TocVC. Still doesn't work.

Country:

let detailsVc = CountryDetailsVC()
...
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue .destination is TocVC {
        let vc = segue.destination as? TocVC
        vc?.countryVC = self
    }
}

TocVC:

var countryVC: Country?
...
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        selectedItem = contents[indexPath.row]

        Country().detailsVc.labelUpdate(dataFrom: selectedItem)

CountryDetailsVC:

@IBOutlet weak var label: UILabel!

    func labelUpdate(dataFrom: String) {
        print("dataFrom: \(dataFrom)")
        self.label.text = dataFrom
    }
06153
  • 189
  • 2
  • 9
  • where you are creating CountryDetailsVC object first time ? you need that object to call labelUpdate method. – vivekDas Aug 09 '18 at 09:59
  • I think the issue is you creating a new instance of your view controller by calling `let VC = CountryDetailsVC()`, which is not in the view hierarchy and therefor its `label` property is `nil` when you are trying to set the text. Are you using Storyboard or where do you instantiate the view controllers? – Rob Aug 09 '18 at 09:59
  • I've created View Controllers for Menu and Detail Screen in storyboard. Screenshot attached. – 06153 Aug 09 '18 at 10:17
  • @06153 You should be be passing the value to the existing CountryDetailsVC(), not to the new instance. Get the existing from the parent. – Anuraj Aug 09 '18 at 10:39
  • @Anuraj you mean `CountryDetailsVC().labelUpdate(dataFrom: selectedItem)`? It shows the same error. – 06153 Aug 09 '18 at 13:15

1 Answers1

0

Instead of creating a new instance of CountryDetailsVC in tableView(:didSelectRowAt:), you should store a reference to CountryDetailsVC in your parent view controller in a variable, for example in prepare(for:sender:) of the embed segue and then call your function on that variable later on.

Rob
  • 491
  • 4
  • 14
  • Perhaps I should've mentioned it earlier: I'm not really experienced with Swift. Do I understand you correctly: I prepare a segue to `CountryDetailsVC` in `CountryVC` and store a variable like `let vc = segue.destination as? CountryDetailsVC` in there. Then I call a function from `tableView(:didSelectRowAt:)` to pass value to that segue, right? – 06153 Aug 09 '18 at 12:13
  • Yes, I think that is correct. But you should store the `vc` as class variable of your `CountryVC` so you can access it later. You could then also in `prepare(for:sender:)` pass a reference to `CountryVC` itself to `TocVC` (let's call it `countryVC`) and then in your `tableView(:didSelectRowAt:)` call `countryVC.vc.labelUpdate(dataFrom: selectedItem)` – Rob Aug 09 '18 at 12:25
  • Okay, I've followed your recommendation but it seems I've messed something up. Function `func labelUpdate(dataFrom: String) { print("dataFrom: \(dataFrom)") self.label.text = dataFrom }` in `CountryDetailsVC` now prints proper value of `dataFrom ` but when trying to assign it to label in the next line it still has the same error. – 06153 Aug 09 '18 at 13:24
  • Can you update your question with the changes you made? The issue is most likely that `self.label` is still nil, because you are not accessing the VC instantiated via Storyboard. – Rob Aug 09 '18 at 14:22
  • I've updated my code. Segue works, function is being called but there is some problem with `label.text` – 06153 Aug 09 '18 at 14:38
  • `Country().detailsVc.labelUpdate(dataFrom: selectedItem)` this line will once again create a new instance of your `Country` view controller. You need to pass a reference to itself to the `TocVC`. See my second comment. – Rob Aug 09 '18 at 15:57
  • I've passed a reference to `self` to `TocVC` but it had no effect. Sorry if I got it wrong again. Could you please share an example code? – 06153 Aug 10 '18 at 02:05
  • Eventually I got rid of `ContryDetailsVC` and created a label in `Country` instead. It worked smoothly. Thank you for your help, your hint about passing reference to `self` helped me to figure it out. – 06153 Aug 10 '18 at 06:35