Passing data with protocols without going back to original controller - ios

I've been looking into how delegation works. You define a protocol in controller A, create a delegate variable, and call the function through the delegate. Then, in controller B, you conform to the protocol, implement methods, and then use prepareForSegue to tell controller A that controller B is the delegate.
But this involves A -> B -> A. I need to know how to do A -> B. I've been trying to do this through the following code:
Declare the protocol in controller A
protocol CellDataDelegate {
func userDidTapCell(data: String)
}
Create a delegate variable in A
var cellDelegate: CellDataDelegate? = nil
Call the function in the delegate in A when cell tapped
if cellDelegate != nil {
let cellKey = keys[indexPath.row].cellKey
cellDelegate?.userDidTapCell(data: cellKey)
self.performSegue(withIdentifier: "showDetails", sender: self)
}
Add the delegate to controller B and conform to the method
class DetailsVC: UIViewController, CellDataDelegate
The function:
func userDidTapCell(data: String) {
useData(cellKey: data)
}
The problem here is the last part of the delegation process. I can't use prepareForSegue to do the controllerA.delegate = self part because I don't want to go back to controller A, I need to stay in controller B. So how do I tell controller A that B is the delegate?

Protocol Delegates are usually used to pass data to a previous UIViewController than the present one in the navigation stack(in case of popViewController) because the UIViewController to which the data is to be sent needs to be present in the memory. In your case you havn't initialised UIViewController B in memory for the method of protocol delegate to execute.
There are simple ways to send data to the next UIViewControllers in the navigation stack.
Your UIViewController B should have a receiving variable to store data sent from the UIViewController A
class DestinationVC : UIViewController
{
receivingVariable = AnyObject? // can be of any data type depending on the data
}
Method 1: Using Storyboard ID
let destinationVC = self.storyboard.instantiateViewControllerWithIdentifier("DestinationVC") as DestinationVC
destinationVC.receivingVariable = dataInFirstViewControllerToBePassed
self.navigationController.pushViewController(destinationVC , animated: true)
Method 2: Using prepareForSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!)
{
let destinationVC = segue.destinationViewController as DestinationVC
destinationVC.receivingVariable = dataInFirstViewControllerToBePassed
}
Multiple segues from UIViewController A to any other UIViewController will cause in execution of prepareForSegue every single time and might crash the application as other classes of UIViewControllers would have no such parameters as receivingVariable which is present in UIViewController B.
This can be easily countered; use of multiple segues can be done simply using if else or switch modules on segue.identifier which is a parameter of segue.
Note: UILabel, UIButton and another other UI element's attribute cannot be assigned in this manner because these element load in the memory in the func loadView() of UIViewController lifecycle as they are not set to initialise when you initialise the class of UIViewController B as mentioned above.

I don't think you need to use delegate pattern here. If you are trying to achieve this. You have some cells on view controller A and now you want to display details of cell(on click) in view controller B. You can declare cell key as the property in view controller B.
class B: UIViewController {
let cellKey: String!
}
And set the above key in prepare for segue method
if (segue.identifier == "segueToViewControllerB") {
let vc = segue.destinationViewController as B
vc.cellKey= "1"
}

I think you are misunderstanding the point of the question you referenced. The question above explained the what is happening in a lot of detail, but here is a short answer, for those who are lazy: do NOT you prepareForSegue to pass information bottom to top (i.e. from child view controller to parent), but most certainly DO use it to pass top to bottom.

Related

How to modify a cell of UITableView from another ViewController

In VC#1, I have a UITableView. When I tap on a cell, I am brought to VC#2 where information about that cell is displayed.
I want to be able to press a button in VC#2 which changes the title of the cell it corresponds with in VC#1, but I am confused on how to do this?
Should I create a variable in VC#2 to save the indexPath for the cell that was tapped, and then call a function in VC#1 from VC#2 that uses that indexPath to update the cell? If I did this, wouldn't VC#1 need to be static so I know I'm modifying the right instance of VC#1? I'm using a push segue and a navigation controller to go back, so creating a new instance of VC#1 wouldn't reference the same VC im trying to modify as I believe?
Is there an easier way to do this?
You should use the delegate pattern.
VC1 should know what cell that VC2 is showing. You should have an IndexPath property in VC1 that stores what cell is VC2 currently displaying, right?
Now, create a protocol called VC2Delegate:
protocol VC2Delegate : class {
func titleDidChange(_ vc2: VC2, to title: String)
}
Now, add this property in VC2:
weak var delegate: VC2Delegate?
Now, when you think the title of the cell should change, call the delegate:
delegate?.titleDidChange(self, to: "Some Title")
That's all for VC2.
Make VC1 conform to VC2Delegate:
extension VC1: VC2Delegate {
func titleDidChange(_ vc2: VC2, to title: String) {
// set the text of the table cell here...
}
}
Now, when you are passing data to VC2 from VC1, probably in the prepareForSegue method, do
vc2.delegate = self
Learn more about delegates here.
You can pass every data you want through view controllers using delegates
First create a protocol whatever you want
protocol ViewControllerDelegate {
func getSelected(value:Int)
}
Create a variable from your ViewController you want pass the data
var delegate: ViewControllerDelegate?
On didSelectRowAt method you will do
if delegate != nil {
delegate.getSelected(value: indexPath.row)
}
self.navigationController?.popViewController(animated: true)
On ViewController that will receive data you have to do this
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? SecondViewController {
vc.delegate = self
}
}
extension YourViewController: ViewControllerDelegate {
fun getSelected(value:Int) {
// Get value from another view controller and manage it
}
}
This code is in Swift 4
If you don't understand something let me know
It's wrong approach you are pursuing. You must separate your data layer from your presentation layer. So in VC#2 you edit your visualized data, then VC#1 reloads the data to update its view.
Short answer: You should not do that at all.
View controllers should not modify other view controller's views.
You should modify the data model in VC2, then send a message back to VC1 telling it to update the cell.
(In the push segue you can set up VC1 to be VC2's delegate, then define a protocol that VC2 uses to notify VC1 about the indexPath's of the data model that need to be updated.)

Make a segmented control change the value for another VC [duplicate]

Say I have multiple view controllers in my Swift app and I want to be able to pass data between them. If I'm several levels down in a view controller stack, how do I pass data to another view controller? Or between tabs in a tab bar view controller?
(Note, this question is a "ringer".) It gets asked so much that I decided to write a tutorial on the subject. See my answer below.
Your question is very broad. To suggest there is one simple catch-all solution to every scenario is a little naïve. So, let's go through some of these scenarios.
The most common scenario asked about on Stack Overflow in my experience is the simple passing information from one view controller to the next.
If we're using storyboard, our first view controller can override prepareForSegue, which is exactly what it's there for. A UIStoryboardSegue object is passed in when this method is called, and it contains a reference to our destination view controller. Here, we can set the values we want to pass.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "MySegueID" {
if let destination = segue.destination as? SecondController {
destination.myInformation = self.myInformation
}
}
}
Alternatively, if we're not using storyboards, then we're loading our view controller from a nib. Our code is slightly simpler then.
func showNextController() {
let destination = SecondController(nibName: "SecondController", bundle: nil)
destination.myInformation = self.myInformation
show(destination, sender: self)
}
In both cases, myInformation is a property on each view controller holding whatever data needs to be passed from one view controller to the next. They obviously don't have to have the same name on each controller.
We might also want to share information between tabs in a UITabBarController.
In this case, it's actually potentially even simpler.
First, let's create a subclass of UITabBarController, and give it properties for whatever information we want to share between the various tabs:
class MyCustomTabController: UITabBarController {
var myInformation: [String: AnyObject]?
}
Now, if we're building our app from the storyboard, we simply change our tab bar controller's class from the default UITabBarController to MyCustomTabController. If we're not using a storyboard, we simply instantiate an instance of this custom class rather than the default UITabBarController class and add our view controller to this.
Now, all of our view controllers within the tab bar controller can access this property as such:
if let tbc = self.tabBarController as? MyCustomTabController {
// do something with tbc.myInformation
}
And by subclassing UINavigationController in the same way, we can take the same approach to share data across an entire navigation stack:
if let nc = self.navigationController as? MyCustomNavController {
// do something with nc.myInformation
}
There are several other scenarios. By no means does this answer cover all of them.
This question comes up all the time.
One suggestion is to create a data container singleton: An object that gets created once and only once in the life of your application, and persists for the life of your app.
This approach is well suited for a situation when you have global app data that needs to be available/modifiable across different classes in your app.
Other approaches like setting up one-way or 2-way links between view controllers are better suited to situations where you are passing information/messages directly between view controllers.
(See nhgrif's answer, below, for other alternatives.)
With a data container singleton, you add a property to your class that stores a reference to your singleton, and then use that property any time you need access.
You can set up your singleton so that it saves it's contents to disk so that your app state persists between launches.
I created a demo project on GitHub demonstrating how you can do this. Here is the link:
SwiftDataContainerSingleton project on GitHub
Here is the README from that project:
SwiftDataContainerSingleton
A demonstration of using a data container singleton to save application state and share it between objects.
The DataContainerSingleton class is the actual singleton.
It uses a static constant sharedDataContainer to save a reference to the singleton.
To access the singleton, use the syntax
DataContainerSingleton.sharedDataContainer
The sample project defines 3 properties in the data container:
var someString: String?
var someOtherString: String?
var someInt: Int?
To load the someInt property from the data container, you'd use code like this:
let theInt = DataContainerSingleton.sharedDataContainer.someInt
To save a value to someInt, you'd use the syntax:
DataContainerSingleton.sharedDataContainer.someInt = 3
The DataContainerSingleton's init method adds an observer for the UIApplicationDidEnterBackgroundNotification. That code looks like this:
goToBackgroundObserver = NSNotificationCenter.defaultCenter().addObserverForName(
UIApplicationDidEnterBackgroundNotification,
object: nil,
queue: nil)
{
(note: NSNotification!) -> Void in
let defaults = NSUserDefaults.standardUserDefaults()
//-----------------------------------------------------------------------------
//This code saves the singleton's properties to NSUserDefaults.
//edit this code to save your custom properties
defaults.setObject( self.someString, forKey: DefaultsKeys.someString)
defaults.setObject( self.someOtherString, forKey: DefaultsKeys.someOtherString)
defaults.setObject( self.someInt, forKey: DefaultsKeys.someInt)
//-----------------------------------------------------------------------------
//Tell NSUserDefaults to save to disk now.
defaults.synchronize()
}
In the observer code it saves the data container's properties to NSUserDefaults. You can also use NSCoding, Core Data, or various other methods for saving state data.
The DataContainerSingleton's init method also tries to load saved values for it's properties.
That portion of the init method looks like this:
let defaults = NSUserDefaults.standardUserDefaults()
//-----------------------------------------------------------------------------
//This code reads the singleton's properties from NSUserDefaults.
//edit this code to load your custom properties
someString = defaults.objectForKey(DefaultsKeys.someString) as! String?
someOtherString = defaults.objectForKey(DefaultsKeys.someOtherString) as! String?
someInt = defaults.objectForKey(DefaultsKeys.someInt) as! Int?
//-----------------------------------------------------------------------------
The keys for loading and saving values into NSUserDefaults are stored as string constants that are part of a struct DefaultsKeys, defined like this:
struct DefaultsKeys
{
static let someString = "someString"
static let someOtherString = "someOtherString"
static let someInt = "someInt"
}
You reference one of these constants like this:
DefaultsKeys.someInt
Using the data container singleton:
This sample application makes trival use of the data container singleton.
There are two view controllers. The first is a custom subclass of UIViewController ViewController, and the second one is a custom subclass of UIViewController SecondVC.
Both view controllers have a text field on them, and both load a value from the data container singlelton's someInt property into the text field in their viewWillAppear method, and both save the current value from the text field back into the `someInt' of the data container.
The code to load the value into the text field is in the viewWillAppear: method:
override func viewWillAppear(animated: Bool)
{
//Load the value "someInt" from our shared ata container singleton
let value = DataContainerSingleton.sharedDataContainer.someInt ?? 0
//Install the value into the text field.
textField.text = "\(value)"
}
The code to save the user-edited value back to the data container is in the view controllers' textFieldShouldEndEditing methods:
func textFieldShouldEndEditing(textField: UITextField) -> Bool
{
//Save the changed value back to our data container singleton
DataContainerSingleton.sharedDataContainer.someInt = textField.text!.toInt()
return true
}
You should load values into your user interface in viewWillAppear rather than viewDidLoad so that your UI updates each time the view controller is displayed.
Another alternative is to use the notification center (NSNotificationCenter) and post notifications. That is a very loose coupling. The sender of a notification doesn't need to know or care who's listening. It just posts a notification and forgets about it.
Notifications are good for one-to-many message passing, since there can be an arbitrary number of observers listening for a given message.
Swift 4
There are so many approaches for data passing in swift. Here I am adding some of the best approaches of it.
1) Using StoryBoard Segue
Storyboard segues are very much useful for passing data in between Source and Destination View Controllers and vice versa also.
// If you want to pass data from ViewControllerB to ViewControllerA while user tap on back button of ViewControllerB.
#IBAction func unWindSeague (_ sender : UIStoryboardSegue) {
if sender.source is ViewControllerB {
if let _ = sender.source as? ViewControllerB {
self.textLabel.text = "Came from B = B->A , B exited"
}
}
}
// If you want to send data from ViewControllerA to ViewControllerB
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.destination is ViewControllerB {
if let vc = segue.destination as? ViewControllerB {
vc.dataStr = "Comming from A View Controller"
}
}
}
2) Using Delegate Methods
ViewControllerD
//Make the Delegate protocol in Child View Controller (Make the protocol in Class from You want to Send Data)
protocol SendDataFromDelegate {
func sendData(data : String)
}
import UIKit
class ViewControllerD: UIViewController {
#IBOutlet weak var textLabelD: UILabel!
var delegate : SendDataFromDelegate? //Create Delegate Variable for Registering it to pass the data
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
textLabelD.text = "Child View Controller"
}
#IBAction func btnDismissTapped (_ sender : UIButton) {
textLabelD.text = "Data Sent Successfully to View Controller C using Delegate Approach"
self.delegate?.sendData(data:textLabelD.text! )
_ = self.dismiss(animated: true, completion:nil)
}
}
ViewControllerC
import UIKit
class ViewControllerC: UIViewController , SendDataFromDelegate {
#IBOutlet weak var textLabelC: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func btnPushToViewControllerDTapped( _ sender : UIButton) {
if let vcD = self.storyboard?.instantiateViewController(withIdentifier: "ViewControllerD") as? ViewControllerD {
vcD.delegate = self // Registring Delegate (When View Conteoller D gets Dismiss It can call sendData method
// vcD.textLabelD.text = "This is Data Passing by Referenceing View Controller D Text Label." //Data Passing Between View Controllers using Data Passing
self.present(vcD, animated: true, completion: nil)
}
}
//This Method will called when when viewcontrollerD will dismiss. (You can also say it is a implementation of Protocol Method)
func sendData(data: String) {
self.textLabelC.text = data
}
}
Instead of creating a data controller singelton I would suggest to create a data controller instance and pass it around. To support dependency injection I would first create a DataController protocol:
protocol DataController {
var someInt : Int {get set}
var someString : String {get set}
}
Then I would create a SpecificDataController (or whatever name would currently be appropriate) class:
class SpecificDataController : DataController {
var someInt : Int = 5
var someString : String = "Hello data"
}
The ViewController class should then have a field to hold the dataController. Notice that the type of dataController is the protocol DataController. This way it's easy to switch out data controller implementations:
class ViewController : UIViewController {
var dataController : DataController?
...
}
In AppDelegate we can set the viewController's dataController:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if let viewController = self.window?.rootViewController as? ViewController {
viewController.dataController = SpecificDataController()
}
return true
}
When we move to a different viewController we can pass the dataController on in:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
...
}
Now when we wish to switch out the data controller for a different task we can do this in the AppDelegate and do not have to change any other code that uses the data controller.
This is of course overkill if we simply want to pass around a single value. In this case it's best to go with nhgrif's answer.
With this approach we can separate view form the logic part.
As #nhgrif pointed out in his excellent answer, there are lots of different ways that VCs (view controllers) and other objects can communicate with each other.
The data singleton I outlined in my first answer is really more about sharing and saving global state than about communicating directly.
nhrif's answer lets you send information directly from the source to the destination VC. As I mentioned in reply, it's also possible to send messages back from the destination to the source.
In fact, you can set up an active one-way or 2-way channel between different view controllers. If the view controllers are linked via a storyboard segue, the time to set up the links is in the prepareFor Segue method.
I have a sample project on Github that uses a parent view controller to host 2 different table views as children. The child view controllers are linked using embed segues, and the parent view controller wires up 2-way links with each view controller in the prepareForSegue method.
You can find that project on github (link). I wrote it in Objective-C, however, and haven't converted it to Swift, so if you're not comfortable in Objective-C it might be a little hard to follow
SWIFT 3:
If you have a storyboard with identified segues use:
func prepare(for segue: UIStoryboardSegue, sender: Any?)
Although if you do everything programmatically including navigation between different UIViewControllers then use the method:
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool)
Note: to use the second way you need to make your UINavigationController, you are pushing UIViewControllers on, a delegate and it needs to conform to the protocol UINavigationControllerDelegate:
class MyNavigationController: UINavigationController, UINavigationControllerDelegate {
override func viewDidLoad() {
self.delegate = self
}
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
// do what ever you need before going to the next UIViewController or back
//this method will be always called when you are pushing or popping the ViewController
}
}
It depends when you want to get data.
If you want to get data whenever you want, can use a singleton pattern. The pattern class is active during the app runtime. Here is an example of the singleton pattern.
class AppSession: NSObject {
static let shared = SessionManager()
var username = "Duncan"
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print(AppSession.shared.username)
}
}
If you want to get data after any action, can use NotificationCenter.
extension Notification.Name {
static let loggedOut = Notification.Name("loggedOut")
}
#IBAction func logoutAction(_ sender: Any) {
NotificationCenter.default.post(name: .loggedOut, object: nil)
}
NotificationCenter.default.addObserver(forName: .loggedOut, object: nil, queue: OperationQueue.main) { (notify) in
print("User logged out")
}
The way that I would do it would be instead of passing data between view controllers, I would just declare a variable globally. You can even do this with a function!
For example:
var a = "a"
func abc() {
print("abc")
}
class ViewController: UIViewController {
}

Passing Data from to another View Controller without changing view (Swift)

I have a searchViewController where I search for users and UITableView gets updated dynamically with user information. The cell for the UITableView is custom - it has a UIImage, the usernameLabel, and a button called "Add".
What I want is that if the user clicks on the add button of the cell, it should pass the user information on that cell (image and username) to another view controller that has a UITableView that is a friend list.
However, so far the only way I know is by using performSegue to pass the data on to the other viewController holding the friendlist UITable. But by this method, every time I click the add button it segues to the other view controller which I don't want. I want it to stay on the searchViewController when the add button is clicked - I only want the data to be passed.
Is there any way I can do this? Is using NSUserDefaults advisable for passing data of this sort?
For simplicity I will use FriendListVC and AddVC
If you are going to your AddVC from FriendListVC via a bar button item or something and your stack looks like:-
FriendListVC -> AddVC
There are two approaches you can use:-
1) Create a delegate of your friendListVC in your addVC and modify the friendListVC datasource on any changes there
2) Or, and I recommend this approach, just reload your FriendListVC datasource on it's viewWillAppear. viewWillAppear will get called even if you navigate back. Thus even if you add a deleteVC in the future and navigate back, the viewWillAppear will perform the updates and it will be independent of any other VC
Hope that helps
Use delegate for passing data between view controllers. you can find this useful
Passing data between 2 UIViewController using delegate and protocol
you can use NSUserDefaults but delegate pattern is better than this.
You can use callback method best and easy way to pass data one controller to another
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
let viewControllerB = segue.destinationViewController as! ViewControllerB
viewControllerB.callback = { message in
//Do what you want in here!
}
}
In ViewControllerB:
var callback : (String -> Void)?
#IBAction func search(sender: AnyObject) {
callback?("Pass data to view controller1")
self.dismissViewControllerAnimated(true, completion: nil)
}
The easiest way to do this is by making an instance of the view controller that you want to pass data to, in the current view controller. I will write you a sample code for this.
class yourTableViewController: UITableViewController {
var controllerToPassData: UIViewController()
func clickTableButton(sender: UIButton) {
controllerToPassData.count += 1
}
}
class controllerwhereDataisPassed: UIViewController {
var count: Int!
}
Pick the instance of the controller where you want to pass data to from the navigationController stack and use this code.

How to send values to a parent view controller in Swift

I am working on a settings view controller screen for my iOS app written in swift. I am using a navigation controller to run the main settings table view which shows the cell titled, "Input Method." The current method is listed on the right of the cell. They can click the cell to go to the next view controller where they can select the input method that they'd like.
From here, there are two sections. The first is the input method to choose (touchscreen or joystick). The second section is joystick specific on whether or not the person is a lefty or righty. I don't want to have the vc unwind when they choose one box because they may choose one in another section too.
My question: How can I update the text field in the parent controller from the child controller.
Problems I'm having for optional solutions:
let parentVC: UIViewController = (self.navigationController?.parentViewController)!
parentVC.inputMethod.text? = cellSelected // This doesn't work because it cannot find the label inputMethod.
viewDidLoad() will cause a lag and the user sees the old method before it changes.
I cannot find out how to run a segue when someone clicks the back button at the upper left hand side in the navigation controller, since the navigation controller controls the segue.
It is not a good idea to cast the parent view controller, even when you are sure which class represents. I'll do it with a protocol:
In the child controller add a protocol like:
protocol ChildNameDelegate {
func dataChanged(str: String)
}
class ChildClass {
weak var delegate: ChildNameDelegate?
func whereTheChangesAreMade(data: String) {
delegate?.dataChanged(data)
}
}
And in the parent:
class ParentClass: ChildNameDelegate {
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
guard let segueId = segue.identifier else { return }
switch segueId {
case "childSegue":
let destVC = segue.destinationViewController as! ChildClass
destVC.delegate = self
break
default:
break
}
}
// Child Delegate
func dataChanged(str: String) {
// Do whatever you need with the data
}
}
You need to cast the parentViewController to whatever custom class it has. For example, if the parent has the class ExampleParentController, you would write:
let parentVC = (self.navigationController?.parentViewController)! as! ExampleParentController
parentVC.inputMethod.text? = cellSelected
I found a solution here: Modifing one variable from another view controller swift
http://www.raywenderlich.com/115300/swift-2-tutorial-part-3-tuples-protocols-delegates-and-table-views
Instead of trying to access the view controller directly (which would be easier if it weren't returning a nil for the view controller) you can use a delegate method to adjust the variables.
The delegate worked like a charm!

How do I get the views inside a container in Swift?

I have a Container View that I popped into my storyboard. There's a wonderful little arrow that represents the embed segue to another scene. That scene's top level object is controlled by a custom UIViewController. I want to call a method that's implemented in my custom class. If I have access to the container, how do I get a reference to what's inside?
You can use prepareForSegue, a method in UIViewController, to gain access to any UIViewController being segued to from your current view controller, this includes embed segues.
From the documentation about prepareForSegue:
The default implementation of this method does nothing. Your view controller overrides this method when it needs to pass relevant data to the new view controller. The segue object describes the transition and includes references to both view controllers involved in the segue.
In your question you mentioned needing to call a method on your custom view controller. Here's an example of how you could do that:
1. Give your embed segue a identifier. You can do this in the Interface Builder by selecting your segue, going to the Attributes Editor and looking under Storyboard Embed Segue.
2. Create your classes something like:
A reference is kept to embeddedViewController so myMethod can be called later. It's declared to be an implicitly unwrapped optional because it doesn't make sense to give it a non-nil initial value.
// This is your custom view controller contained in `MainViewController`.
class CustomViewController: UIViewController {
func myMethod() {}
}
class MainViewController: UIViewController {
private var embeddedViewController: CustomViewController!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let vc = segue.destination as? CustomViewController,
segue.identifier == "EmbedSegue" {
self.embeddedViewController = vc
}
}
// Now in other methods you can reference `embeddedViewController`.
// For example:
override func viewDidAppear(animated: Bool) {
self.embeddedViewController.myMethod()
}
}
3. Set the classes of your UIViewControllers in IB using the Identity Inspector. For example:
And now everything should work. Hope that helps!
ABaker's answer gives a great way for the parent to learn about the child. For code in the child to reach the parent, use self.parent (or in ObjC, parentViewController).

Resources