Passing Data to View Controllers That Are Embedded in Container Views

Passing Data to view controllers that are embedded in container views

In your Storyboard, when you embed a VC in a ContainerView, you also see a "segue" connecter. When the root VC loads, you will get a call to prepare for segue for that.

Give each storyboard-created segue an Identifier - such as "infoViewEmbedSegue", "feedViewEmbedSegue", etc.

In your root VC, I'm guessing that

var infos: BusinessProfilesDetails!
var feeds: BusinessProfilePostsFeed!

are variables to reference the content of infoView? If so, in prepare() you want to:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

// get a reference to the embedded PageViewController on load

if let vc = segue.destination as? BusinessProfilesDetails,
segue.identifier == "infoViewEmbedSegue" {
self.infos = vc
// if you already have your data object
self.infos.otherUser = theDataDict
}

if let vc = segue.destination as? BusinessProfilePostsFeed,
segue.identifier == "feedViewEmbedSegue" {
self.feeds = vc
// if you already have your data object
self.feeds.otherUser = theDataDict
}

// etc

}

Now you'll have persistent references to the actual View Controllers embedded in your Container Views, in case you want access to them in other parts of your root VC, e.g.:

@IBAction func btnTapped(_ sender: Any) {
self.feeds.otherUser = theDataDict
}

Pass data between ViewController and ContainerViewController

All you need to do is keep a reference to Container in your master view controller.

That is, you should add an instance variable to Master that will hold a reference to the view controller, not just the view. You'll need to set it in prepareForSegue.

So the beginning of Master View Controller would look something like this:

class Master: UIViewController, ContainerToMaster {

@IBOutlet var containerView: UIView!

var containerViewController: Container?

@IBOutlet var labelMaster: UILabel!

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "containerViewSegue" {
containerViewController = segue.destinationViewController as? Container
containerViewController!.containerToMaster = self
}
}

And then in your button function, simply change the label using the variable you just added.

Example:

@IBAction func button_Container(sender: AnyObject) {
containerViewController?.changeLabel("Nice! It's work!")
}

This means you can get rid of your MasterToContainer protocol too.

I tested this code, so I know it works, but unfortunately I am an Objective-C dev, and know nothing about best practices in Swift. So I don't know if this is the best way to go about it, but it certainly works.

Edit:

Here's the exact code I've tested:

Master.swift:

import UIKit

class Master: UIViewController, ContainerToMaster {

@IBOutlet var containerView: UIView!
@IBOutlet var labelMaster: UILabel!
var containerViewController: Container?

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "containerViewSegue" {
containerViewController = segue.destinationViewController as? Container
containerViewController!.containerToMaster = self
}
}

@IBAction func button_Container(sender: AnyObject) {
containerViewController?.changeLabel("Nice! It's work!")
}

func changeLabel(text: String) {
labelMaster.text = text
}
}

Container.swift:

import UIKit

protocol ContainerToMaster {
func changeLabel(text:String)
}

class Container: UIViewController {

@IBOutlet var labelContainer: UILabel!
var containerToMaster:ContainerToMaster?

@IBAction func button_Master(sender: AnyObject) {
containerToMaster?.changeLabel("Amazing! It's work!")
}

func changeLabel(text: String) {
labelContainer.text = text
}
}

How to pass data from View controller to Container?

Set segue identifier in storyboard, first of all. You could find the field where can set identifier after click segue link.

View Controller which will call next ViewController

func showSomeViewController() {
self.performSegueWithIdentifier("TestContainer", sender: self);
}

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

if (segue.identifier == "TestContainer")
{
let vc: TestViewController = segue.destinationViewController as! TestViewController
vc.tmpString = "say ho"
}

}

and TestViewController

class TestViewController: UIViewController {

var tmpString: String!

override func viewDidLoad() {
super.viewDidLoad()

// Do any additional setup after loading the view.
}
}

Share Data Between View Controller and Container View

You are on the right track to setup the delegate.

In Storyboard:

  • create a Segue of type Embed Segue from the ContainerView to the ViewController to embed.
  • Select the segue and set a Identifier.

In Code, in prepareForSegue():

  • Set the myContainerView propert
  • Set the delegate

    class ViewController: UITableViewController, ContainerViewDelegate {
    var myContainerView:ContainerView? // we do initialize it in prepareForSegue()
    var id: String!

    override func viewDidLoad() {
    super.viewDidLoad()
    id = "123"
    // myContainerView.delegate = self //myContainerView is nil here. We set it in prepareForSegue
    }

    func initialize() -> String {
    return id
    }

    let embedSegueID = "the identifier you set to your embed segue in Storyboard"

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if (segue.identifier == embedSegueID) {
    let dvc = segue.destination as! ContainerView
    // Now you have a pointer to EmbeddedViewController.
    // You can save the reference to it, or pass data to it.
    myContainerView = dvc
    myContainerView.delegate = self
    }
    print(segue.identifier)
    }
    }

If you want to pass data from your ViewController to AnotherViewController which is embedded in your destinationViewController (how I understand your question), I would:

  • pass it to destinationViewController as in the example above (prepareForSegue)
  • than pass it to AnotherViewController in viewWillAppear() from within destinationViewController.

In this case, how I understand your usecase, you do not need the delegate and can remove all delegate related code (initialize, the ContainerViewDelegate protocol, the property var delegate: ContainerViewDelegate! and all calls to it.

What's the easiest way to pass data between two simultaneously displayed view controllers?


Creating Delegates

Start with creating delegates for both of your container ViewControllers. Don't forget to add : class. If you didn't do it, you wouldn't be able to create weak delegate variable:

protocol TopViewControllerDelegate: class {
func sendMessage(_ string: String)
}
protocol BottomViewControllerDelegate: class {
func sendMessage(_ string: String)
}

Now for every container ViewController create weak delegate variable

class TopViewController: UIViewController {
weak var delegate: TopViewControllerDelegate?
...
}

class BottomViewController: UIViewController {
weak var delegate: BottomViewControllerDelegate?
...
}

then for TopVC implement Bottom's delegate and for BottomVC Top's.

extension TopViewController: BottomViewControllerDelegate {
func sendMessage(_ string: String) {
// do something
}
}
extension BottomViewController: TopViewControllerDelegate {
func sendMessage(_ string: String) {
// do something
}
}

Assigning Delegates

Your segues between main ViewController and containers should have their own identifiers: EmbedTop, EmbedBottom.

So in your WrapperViewController create variable for your Top and Bottom ViewController and override method prepare(for:sender:) and inside assign these variables

class WrapperViewController: UIViewController {

var topVC: TopViewController?
var bottomVC: BottomViewController?

...

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "EmbedTop" {
topVC = segue.destination as! TopViewController
} else if segue.identifier == "EmbedBottom" {
bottomVC = segue.destination as! BottomViewController
}
}

}

finally in viewDidAppear set delegate of TopVC's as BottomVC and of BottomVC's as TopVC

override func viewDidAppear(_ animated: Bool) {
topVC.delegate = bottomVC
bottomVC.delegate = topVC
}

Now your two ViewControllers can speak with each other! :-)


Example:

class BottomViewController: UIViewController {
...
func speakToTop() {
delegate?.sendMessage("Hi Top!")
}
}

Swift: How to pass data to PageViewController which is embedded in ContainerView?

Somehow during my own brainstorm on this problem, I found out that I ommited the most straightforward solution:

1) Create C and D instances inside A

2) Pass them to B using prepareForSegue method

3) In B assign them to according VC's

4) Voila, I can access their variables and functions right from A

how to load child view controllers or container views - Swift

As i understand there are 2 container views and you want to see the blue view when the segmented is first and you want to see the green view when the segmented is second if it is true the solution is like this;

You should init the uivew(hold on the ctrl and drag it to viewcontroller for both blue and green container view)(you need to add 2 different container view) and write

if segmented.selectedIndex == 0 { 

greenView.isHidden = true
blueView.isHidden = false

} else if segmented.selectedIndex == 1 {

greenView.isHidden = false
blueView.isHidden = true
}


Related Topics



Leave a reply



Submit