Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I pass data from a parent view controller to an embedded view controller in Swift?

I have a view controller embedded in another VC. I would like to get the value of a variable from the main VC inside the embedded one. Specifically, I would like to change the text of label2 based on the value of label1.

I tried with "prepareForSegue", but it seems it's not triggered for embedded view controllers. I tried to isolate the problem in a test project:

enter image description here

Code for main VC:

class MyViewController: UIViewController {
    @IBOutlet weak var label1: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
        label1.text = "Hello"
    }
}

Code for embedded VC:

class EmbeddedVC: UIViewController {
    @IBOutlet weak var label2: UILabel!

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

Thanks for your help :)

like image 304
Eric Avatar asked Jan 27 '17 08:01

Eric


2 Answers

A way to achiеve this is to get the child view controller instance in the parent's viewDidLoad. It appears that the parent's viewDidLoad: gets called after the child's viewDidLoad:, which means the label is already created in the child's view.

override func viewDidLoad() {
    super.viewDidLoad()

    if let childVC = self.childViewControllers.first as? ChildVC {
        childVC.someLabel.text = "I'm here. Aye-aye."
    }   
}
like image 145
Denislava Shentova Avatar answered Sep 20 '22 18:09

Denislava Shentova


First of all you can't set directly EmbeddedVC's lable2.text In prepareForSegue

because call sequence following below

  1. MainVC's prepareForSeque this time EmbeddedVC's label2 is nil
  2. EmbeddedVC's viewDidLoad called then label2 loaded
  3. MainVC's viewDidLoad called then label1 loaded

so if you assign MainVC's label1.text to EmbeddedVC's label2.text in prepareForSeque both label1 and label2 are nil so did not work

There are two way to solve this question

First Solution MainViewController has EmbeddedVC and when MainVC's viewDidLoad called, assign label1.text to embeddedVC.label2.text

class MyViewController: UIViewController {
    @IBOutlet weak var label1: UILabel!
    var embeddedVC: EmbeddedViewController? = nil

    override func viewDidLoad() {
        super.viewDidLoad()
        label1.text = "Hello"
        embeddedVC?.label2.text = label1.text
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let embeddedVC = segue.destination as? EmbeddedViewController {
            self.embeddedVC = embeddedVC
        }
    }
}

class EmbeddedViewController: UIViewController {
    @IBOutlet weak var label2: UILabel!

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

Second Solution, use protocol and get MainVC's label text when viewWillAppear or viewDidAppear (later viewDidLoad called)

protocol EmbeddedVCDelegate: class {
    func labelText() -> String?
}

class MyViewController: UIViewController, EmbeddedVCDelegate {
    @IBOutlet weak var label1: UILabel!

    // MARK: EmbeddedVCDelegate
    func labelText() -> String? {
        return label1.text
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        label1.text = "Hello"
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let embeddedVC = segue.destination as? EmbeddedViewController {
            embeddedVC.delegate = self
        }
    }
}

class EmbeddedViewController: UIViewController {
    @IBOutlet weak var label2: UILabel!
    weak var delegate: EmbeddedVCDelegate? = nil
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        label2.text = delegate?.labelText()
    }
}
like image 33
Cruz Avatar answered Sep 18 '22 18:09

Cruz