I have been struggling with this for a few days and have received valuable help on the way from S.O. I have made the simplest possible project to reduce the possibilities of it being a typo.
All my project is, is a ViewController that holds a container view hooked to a childViewController. The "parent" ViewController is set as the delegate of the childViewController. In the viewDidLoad of the child I am passing a value which is just a string. This string should be passed on to the parent and printed on the console. Here are the files.
ViewController.h
#import <UIKit/UIKit.h>
#import "ChildViewController.h"
@interface ViewController : UIViewController <ChildViewControllerDelegate>
@end
ViewController.m
#import "ViewController.h"
@interface ViewController ()
@property NSString *myValueRetrieved;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
ChildViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:@"ChildVC"];
controller.delegate = self;
NSLog(@"Here is my value: %@",self.myValueRetrieved);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void) passValue:(NSString *)theValue{
self.myValueRetrieved = theValue;
}
@end
ChildViewController.h
#import <UIKit/UIKit.h>
@protocol ChildViewControllerDelegate;
@interface ChildViewController : UIViewController
@property (weak)id <ChildViewControllerDelegate> delegate;
@end
@protocol ChildViewControllerDelegate <NSObject>
- (void) passValue:(NSString*) theValue;
@end
ChildViewController.m
#import "ChildViewController.h"
@interface ChildViewController ()
@property NSArray *colors;
@end
@implementation ChildViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.delegate passValue:@"Hello"];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
@end
Am I right to think that when the app is launched, the console should log the following message: "here is my value: hello". Am I doing something wrong in terms of logically not getting delegation or is it just a silly typo somewhere? tx
You're assuming that the view is loaded when the view controller is instantiated. That's now how it works. The view gets loaded when it's needed (like to add to the parent view).
But you can force the view to load and make this work. Call -loadViewIfNeeded
on the child view controller right after setting the delegate. That will probably get you what you want:
controller.delegate = self;
[controller loadViewIfNeeded];
NSLog(@"Here is my value: %@",self.myValueRetrieved);
Or, if you do want to call back the delegate in viewDidLoad
, then you'd need to move the NSLog
to the -passValue:
method, since the primary view controller's viewDidLoad
method will have already finished running.
To do this make ParentController a delegate of ChildController. This allows ChildController to send a message back to ParentController enabling us to send data back.
For ParentController to be delegate of ChildController it must conform to ChildController's protocol which we have to specify. This tells ParentController which methods it must implement.
In ChildController.h, below the #import, but above @interface you specify the protocol.
@class ChildController;
@protocol ViewControllerBDelegate <NSObject>
- (void)addItemViewController:(ChildController *)controller didFinishEnteringItem:(NSString *)item;
@end
next still in the ChildController.h you need to setup a delegate property and synthesize in ChildController.h
@property (nonatomic, weak) id <ChildControllerDelegate> delegate;
In ChildController we call a message on the delegate when we pop the view controller.
NSString *itemToPassBack = @"Pass this value back to ParentController";
[self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];
That's it for ChildController. Now in ParentController.h, tell ParentViewController to import Child and conform to its protocol.
import "ChildController.h"
@interface ParentController : UIViewController
In ParentController.m implement the following method from our protocol
- (void)addItemViewController:(ChildController *)controller didFinishEnteringItem:(NSString *)item
{
NSLog(@"This was returned from ChildController %@",item);
}
The last thing we need to do is tell ChildController that ParentController is its delegate before we push ChildController on to nav stack.
ChildController *ChildController = [[ChildController alloc] initWithNib:@"ChildController" bundle:nil];
ChildController.delegate = self
[[self navigationController] pushViewController:ChildController animated:YES];