Search code examples
iosswiftdelegatescontainerssegue

How would I pass data from a View Controller to a Container View?


enter image description here

I'm trying to pass data from a ViewController to a Container within it. When I press the button the delegate sends the data to the container but the container resizes. How would I stop this from happening. I'm thinking a prepareForSegue function but I can't figure out how to implement it but I don’t know if even that would be a solution.

protocol VCDelegate {

    func passData(theData1:String)

}

class ViewController: UIViewController {

    var delegate : VCDelegate?

    @IBAction func sendTextToContainer(_ sender: Any) {

        let ViewC = self.storyboard!.instantiateViewController(withIdentifier: "ViewController") as! ViewController

        let ContainerV = self.storyboard!.instantiateViewController(withIdentifier: "ContainerView") as! ContainerView

        self.present(ContainerV,animated:true,completion: nil)

        ViewC.delegate = ContainerV

        ViewC.delegate?.passData(theData1:"Hello")
    } 
}

class ContainerView: UIViewController, VCDelegate {

    func passData(theData1: String) {

        print(theData1)
        theText.text = theData1

    }

    @IBOutlet weak var theText: UILabel!

    override func viewWillAppear(_ animated: Bool) {

    }

}

Solution

  • You are instantiating a new, second instance of the child view controller. But if you created a “container” in IB, it’s already been instantiated for you.

    There are two ways for the parent view controller to pass data to the child. You can pass the initial data in prepare(for:sender:):

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let destination = segue.destination as? SecondViewControllerProtocol {
            destination.passData(string: "Initial value")
        }
    }
    

    If you want to later update it, you can grab the relevant children:

    @IBAction func didTapButton(_ sender: Any) {
        for child in children {
            if let child = child as? SecondViewControllerProtocol {
                child.passData(string: "Updated value")
            }
        }
    }
    

    (You obviously could save the reference you captured during prepare(for:sender:), too, if you wanted.)

    The second view controller can then update its label accordingly:

    protocol SecondViewControllerProtocol {
        func passData(string: String) 
    }
    
    class SecondViewController: UIViewController {
        @IBOutlet weak var label: UILabel!
    
        private var string: String?
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            // this is in case you passed the data before the label was hooked up
    
            label.text = string
        }
    }
    
    extension SecondViewController: SecondViewControllerProtocol {
        func passData(string: String) {
            self.string = string
    
            guard let label = label else { return }
    
            UIView.transition(with: label, duration: 0.25, options: .transitionCrossDissolve, animations: {
                label.text = string
            }, completion: nil)
        }
    }
    

    That yields:

    enter image description here