I am trying to understand delegation. I have written a small project to try to tackle this. I have also had help from S.O. I am stuck on the very last part of it. My project is simple. We have a main view controller that has a button "start". This button triggers a container view that's hooked to a ContainerViewController. I have done a small animation to get the container to slide from the side. I have another button "back" that makes the container view disappear with the opposite animation. Note, I am copying a lot of code and making up the rest as I am learning, so there may be unnecessary lines, please feel free to comment.
ViewController.h
#import <UIKit/UIKit.h>
#import "ContainerViewController.h"
@interface ViewController : UIViewController <ContainerViewControllerDelegate>
- (IBAction)Start:(id)sender;
- (IBAction)back:(id)sender;
@end
Here is the m file:
#import "ViewController.h"
@interface ViewController ()
@property UIViewController *childView;
@property NSString *myReceivedValue;
@property ContainerViewController *controller;
@property IBOutlet UILabel *myLabel;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.childView = [self.storyboard instantiateViewControllerWithIdentifier:@"childVC"];
self.controller = [self.storyboard instantiateViewControllerWithIdentifier:@"childVC"];
self.controller.delegate = self;
self.childView = [self.childViewControllers lastObject];
[self.childView.view removeFromSuperview];
[self.childView removeFromParentViewController];
self.childView.view.userInteractionEnabled = NO;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)Start:(id)sender {
self.childView.view.frame = CGRectMake(0, 84, 320, 210);
[self.childView didMoveToParentViewController:self];
CATransition *transition = [CATransition animation];
transition.duration = 1;
transition.type = kCATransitionPush;
transition.subtype = kCATransitionFromLeft;
[transition setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
[self.childView.view.layer addAnimation:transition forKey:nil];
[self.view addSubview:self.childView.view];
self.childView.view.userInteractionEnabled = YES;
}
- (IBAction)back:(id)sender {
[self.childView willMoveToParentViewController:nil];
[UIView animateWithDuration:1
delay:0.0
usingSpringWithDamping:1
initialSpringVelocity:1
options:UIViewAnimationOptionCurveEaseIn
animations:^{
self.childView.view.frame = CGRectMake(-320, 84, 320, 210);
} completion:^(BOOL complete){
[self.childView removeFromParentViewController];
}];
}
- (void) passValue:(NSString *) theValue
{
// here is where you receive the data
}
@end
Ok, so the Container View has a pickerView of which it is the delegate and this pickerView has just an array of ten colors to chose from:
h file for the container view:
#import <UIKit/UIKit.h>
@protocol ContainerViewControllerDelegate;
@interface ContainerViewController : UIViewController <UIPickerViewDelegate>
@property NSArray *colors;
@property (weak)id <ContainerViewControllerDelegate> delegate;
@property (weak, nonatomic) IBOutlet UIPickerView *myPickerView;
- (IBAction)chosenCol:(id)sender;
@end
@protocol ContainerViewControllerDelegate <NSObject>
- (void) passValue:(NSString *) theValue;
@end
m file for the container view:
#import "ContainerViewController.h"
@interface ContainerViewController ()
@property NSString *selValue;
@end
@implementation ContainerViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.colors = [[NSArray alloc] initWithObjects:@"blue", @"red", @"green", @"purple", @"black", @"white", @"orange", @"yellow", @"pink", @"violet", nil];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
return 10;
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
return self.colors[row];
}
- (IBAction)chosenCol:(id)sender {
[self.delegate passValue:[self.colors objectAtIndex:[self.myPickerView selectedRowInComponent:0]]];
}
@end
Here is a picture of what it looks like. Note that the "chosen" button is just a provisional one that I put there to make sure everything is hooked alright and that I can log out the chosen color using a button in the container. What I want to do is be able to pass that color to the parent view controller. So that after I dismiss the container with its picker view I have the data stored of what color was chosen. I have had help from someone in S.O. and he's done a very good job of helping me start this up. The only thing I didn't understand is what happens when I receive the data, at the end of the m file of the parent:
- (void) passValue:(NSString *) theValue
{
// here is where you receive the data
}
This is obvioulsy noob question but I really do need it spelt out. How do I actually access the data in the parent. I asked in the comments section, and the reply was (I am changing the class, it was originally uicolor):
"No, you'll receive the data inside the method - (void) passValue:(NSString *) theValue; Put a breakpoint in that method to be sure that it's working, you can access it like this: NSString *myReceivedColor = theValue;
I tried to write word for word "NSString *myReceivedColor = theValue;" but "theValue" is unrecognised.
Ultimately, what I want, is to pass the data back to the parent so that when I hit the button "back", in the parent, the label "you chose" is updated with the chosen color".
I have never touched delegation before so I am lost. Can a charitable soul take the time to explain this last bit in very obvious terms? many thanks
UPDATE-----------------------------------------------------------------------
So, what I am looking at, is to add, at the end of my method for the "back" button,
- (IBAction)back:(id)sender {
[self.childView willMoveToParentViewController:nil];
[UIView animateWithDuration:1
delay:0.0
usingSpringWithDamping:1
initialSpringVelocity:1
options:UIViewAnimationOptionCurveEaseIn
animations:^{
self.childView.view.frame = CGRectMake(-320, 84, 320, 210);
} completion:^(BOOL complete){
[self.childView removeFromParentViewController];
}];
the couple of lines:
self.myReceivedValue = theValue;
self.myLabel.text = self.myReceivedValue;
}
To be able to update the text of myLabel to the the color I've chosen in the view container. It comes back with the error: "use of undeclared identifier "theValue". This is all new to me so I am just copying what people have said on S.O. with the hope of understanding eventually. What am I doing wrong here? tx
It looks like your delegate is nil.
self.childView = [self.storyboard instantiateViewControllerWithIdentifier:@"childVC"];
self.controller = [self.storyboard instantiateViewControllerWithIdentifier:@"childVC"];
self.controller.delegate = self;
You create two instances of "childVC" (a copy/paste typo maybe?) then set the delegate on 'controller' but you use 'childView'. Just change the childView property to be a ContainerViewController and set self.childView.delegate=self.
(BTW its an easy mistake, so many times when you're thinking "why isn't this working??" check that the delegate property is set)
The return value property you're logging is nil b/c you never set it. You have to implement the delegate method, i.e.
-(void) passValue:(nsstring*)theValue
{
self.receivedValue = theValue
}
Also what i was saying about the chosenCol action is that is where you are calling your delegate - your 'back' action does not call this method.