How to Set Up a Simple Delegate to Communicate Between Two View Controllers

How do I set up a simple delegate to communicate between two view controllers?

Simple example...

Let's say the child view controller has a UISlider and we want to pass the value of the slider back to the parent via a delegate.

In the child view controller's header file, declare the delegate type and its methods:

ChildViewController.h

#import <UIKit/UIKit.h>

// 1. Forward declaration of ChildViewControllerDelegate - this just declares
// that a ChildViewControllerDelegate type exists so that we can use it
// later.
@protocol ChildViewControllerDelegate;

// 2. Declaration of the view controller class, as usual
@interface ChildViewController : UIViewController

// Delegate properties should always be weak references
// See http://stackoverflow.com/a/4796131/263871 for the rationale
// (Tip: If you're not using ARC, use `assign` instead of `weak`)
@property (nonatomic, weak) id<ChildViewControllerDelegate> delegate;

// A simple IBAction method that I'll associate with a close button in
// the UI. We'll call the delegate's childViewController:didChooseValue:
// method inside this handler.
- (IBAction)handleCloseButton:(id)sender;

@end

// 3. Definition of the delegate's interface
@protocol ChildViewControllerDelegate <NSObject>

- (void)childViewController:(ChildViewController*)viewController
didChooseValue:(CGFloat)value;

@end

In the child view controller's implementation, call the delegate methods as required.

ChildViewController.m

#import "ChildViewController.h"

@implementation ChildViewController

- (void)handleCloseButton:(id)sender {
// Xcode will complain if we access a weak property more than
// once here, since it could in theory be nilled between accesses
// leading to unpredictable results. So we'll start by taking
// a local, strong reference to the delegate.
id<ChildViewControllerDelegate> strongDelegate = self.delegate;

// Our delegate method is optional, so we should
// check that the delegate implements it
if ([strongDelegate respondsToSelector:@selector(childViewController:didChooseValue:)]) {
[strongDelegate childViewController:self didChooseValue:self.slider.value];
}
}

@end

In the parent view controller's header file, declare that it implements the ChildViewControllerDelegate protocol.

RootViewController.h

#import <UIKit/UIKit.h>
#import "ChildViewController.h"

@interface RootViewController : UITableViewController <ChildViewControllerDelegate>

@end

In the parent view controller's implementation, implement the delegate methods appropriately.

RootViewController.m

#import "RootViewController.h"

@implementation RootViewController

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
ChildViewController *detailViewController = [[ChildViewController alloc] init];
// Assign self as the delegate for the child view controller
detailViewController.delegate = self;
[self.navigationController pushViewController:detailViewController animated:YES];
}

// Implement the delegate methods for ChildViewControllerDelegate
- (void)childViewController:(ChildViewController *)viewController didChooseValue:(CGFloat)value {

// Do something with value...

// ...then dismiss the child view controller
[self.navigationController popViewControllerAnimated:YES];
}

@end

Hope this helps!

IOS Swift, delegate to communicate between two view controllers

Ok, so from comments we got that you didn't set the delegate property, and from the question we know that you are using segues. In order to set the property you need to override one method in your ViewController (the first one) :

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "ConnectDeviceSegue") {
let mapViewController = segue.destinationViewController as! DeviceJoinViewController
mapViewController.delegate = self

}
}

This of course assumes, that your second VC is named SecondViewController - if not, change that part accordingly.

swift delegate beetween two view controller without segue

Typically you set a new view controller's delegate property in prepareForSegue:. You said you're not using a segue, so you'll need to instantiate the second view controller and present it somehow. You can do this by doing something like:

let storyboard = UIStoryboard(name: "AStoryboardName", bundle: nil)
let secondVC = storyboard.instantiateViewControllerWithIdentifier(anIdentifier) as! targetViewController
secondVC.delegate = self
presentViewController(secondVC, animated: true, completion: nil)

You have a testDelegate() method in both view controllers, but you only want it in the first view controller. Then your second view controller can call delegate?.testDelegate() at the appropriate time.

Finally, you typically want to make delegate properties weak, so I would recommend changing var delegate : testProtocol? to weak var delegate: testProtocol?

I would read up on delegation. Here is a relatively simple 5 step process to delegation that may help you:

Delegation in 5 Steps:

object A is the delegate for object B, and object B will send out the messages:

  1. Define a delegate protocol for object B.
  2. Give object B an optional delegate variable. This variable should be weak.
  3. Make object B send messages to its delegate when something interesting happens, such as the user pressing the Cancel or Done buttons, or when it needs a piece of information.
  4. Make object A conform to the delegate protocol. It should put the name of the protocol in its class line and implement the methods from the protocol.
  5. Tell object B that object A is now its delegate (in prepareForSegue(sender)).

iOS communication between to Child Controllers of Collection Controller

What you did is probably the best way of doing it from all perspective but amount of code that is being used. Since your parent (page view controller) is responsible for all the work it is best to also channel all data it needs. Currently that is between 2 view controllers and later it might be between 3. You might also simply change these view controllers but preserve the protocols you use to retrieve the data.

But there is a big catch here. If a number of view controllers will grow then you might find yourself in an issue where previous view controllers are being deallocated (if this is not already going on) so at the end there is no way for view controller D to access view controller A simply because A no longer exists.

What the solution to these things is really depends but from your question I can assume you are passing some data from one view controller to another like onboarding where you are collecting user data through multiple screens. In such case it is best to have a class with all the data needed like:

class MyData {
var dataA: DataA?
var dataB: DataB?
var dataC: DataC?
}

Now the page controller is responsible to create such data and pass them to each of these view controllers that will use/modify the data. So in page view controller:

var myData: MyData = MyData()

func prepareViewControllerA() {
let controller: ViewControllerA...
controller.myData = myData
...
}

Now each of the view controllers will have its own property to access the same data object and modify it. You could also add a delegate to your class so page controller may listen to its events:

protocol MyDataDelegate: class {
func myData(_ sender: MyData, updatedA: DataA?)
func myData(_ sender: MyData, updatedB: DataB?)
func myData(_ sender: MyData, updatedC: DataC?)
func myDataAreFinalized(_ sender: MyData)
}

class MyData {
var dataA: DataA? {
didSet {
delegate?.myData(self, updatedA: dataA)
}
}
var dataB: DataB? {
didSet {
delegate?.myData(self, updatedB: dataB)
}
}
var dataC: DataC? {
didSet {
delegate?.myData(self, updatedC: dataC)
}
}

weak var delegate: MyDataDelegate?

func finalize() {
delegate?.myDataAreFinalized(self)
}

}

And now your page controller can use it:

var myData: MyData = {
let data = MyData()
data.delegate = self
return data
}()

and delegates:

func myData(_ sender: MyData, updatedA: DataA?) {
}
func myData(_ sender: MyData, updatedB: DataB?) {
}
func myData(_ sender: MyData, updatedC: DataC?) {
}
func myDataAreFinalized(_ sender: MyData) {
dismiss(animated: true, completion: nil)
}

Using delegate between sibling view controllers in containers

I ended up finding the solution to the problem after a bit further searching with inspiration from @Anna Dickinson.

Firstly, the containers must be ordered correctly in the storyboard. The container whose view controller implements the delegate protocol must be first in the list and then the other view controller further down.

Then, in the main view controller - the view controller for the view with the containers - the prepareForSegue function is implemented, since it will be triggered as the containers are initialized.

This all of the code remains as above, but the main view controller will be something like the following:

class MainViewController: UIViewController {
var actionVC : FirstViewController! // This is the one, that implements the delegate protocol
var tableVC : SecondViewController! // This is the one, that has a delegate variable
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "firstVC"){
self.actionVC = segue.destinationViewController as FirstViewController
} else if(segue.identifier == "secondVC"){
self.tableVC = segue.destinationViewController as SecondViewController
self.tableVC.delegate = self.actionVC
}
}
}

I'm not sure if the is the right, nor the best way to do this, but it works perfectly for what I need.

Receive delegate method calls from multiple view controllers

This is a best-practice case for a Singleton-pattern
Your DataLoader should implement a static let holding the reference to the same object of self.

static let sharedInstance = DataLoader()

When you want the reference to constantly the same singleton-object. Call it with DataLoader.sharedInstance

Make sure to always use .sharedInstance then and do not forget to wipe any calls of the standard initializer of the class, because it will still make new instances unless you block this behavior programmatically.

Explained

Only inserting the singleton constant (shown above) will NOT make DataLoader class always return the singleton instance. The existing initializer calls like:

   var myDataLoader = DataLoader()

will still instanciate a new object, everytime they are called. The singleton instance is reached with:

   var mySingletonDataLoader = DataLoader.sharedInstance

You could change your standard (non singleton) initializers like:

init() {
NSLog("No instances allowed, please use .sharedInstance for a singleton")
}

Complete solution

The singleton is only one piece to solve the whole problem. The used delegate-pattern works great for sending information from one object to another and therefore delegating the work.
The problem from the questioner needs another broadcasting mechanism from one object to many other object. Therefore he decided to implement the Observer-pattern with NSNotificationCenter

Getting the delegate to work between two view controllers

Your delegation is a bit... Off.

Firstly: Don't override UIKit delegate methods through protocol inheritance. It's pointless. Why not just make your class conform to the specified delegate in the first place?

@protocol ProcessDelegate //No more protocol inheritance!
//...
@end

Secondly: When an object has defined a protocol, a valid instance of that object must be in use by its delegate (or at least passed to it). So, anything that wants to be the delegate of SampleDelegate (really a bad name for a class, by the way) would initialize a valid SampleDelegate object, and call -setDelegate: as though it were any other property.

//#import "SampleDelegate"
@implementation ViewController

- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//make this a property, so it isn't crushed when the function exits.
SampleDelegate *myDelegateObject = [[SampleDelegate alloc]init];
[myDelegateObject setDelegate:self]; //conform to the delegate
}

Thirdly: You don't actually define any delegate methods! What's the point of delegation if there's nothing to delegate!l

@protocol ProcessDelegate 
-(void)someMethod;
@end

Fourth, and most important: Never, ever, ever, ever use the retain, or strong storage specifiers with a delegate! Delegate objects are supposed to be weak or assign to prevent nasty retain cycles.

@property (assign, nomatomic) id delegate;


Related Topics



Leave a reply



Submit