Im trying to implement delegate design pattern.
I have two view controllers as followsm
CallViewViewController
CEPeoplePickerNavigationController
This is my interface definition of CallViewViewController
#interface CallViewViewController ()<CEPeoplePickerNavigationControllerDelegate>{
}
#property(nonatomic)CustomMessageClass * customMessageClassObject;
#end
In my implementation, i have of-course implemented the delegate method
-(void)cePeoplePickerNavigationController:(CEPeoplePickerNavigationController *)peoplePicker didFinishPickingPeople:(NSArray *)peopleArg values:(NSDictionary *)valuesArg
{
NSLog(#"can populate the tableview");
}
This is the interface definition of my second class,
#protocol CEPeoplePickerNavigationControllerDelegate;
#interface CEPeoplePickerNavigationController : UIViewController <UITableViewDelegate,UITableViewDataSource>{
id<CEPeoplePickerNavigationControllerDelegate> peoplePickerDelegate;
ABAddressBookRef addressBook;
}
#property (nonatomic, assign) id<CEPeoplePickerNavigationControllerDelegate> peoplePickerDelegate;
#property (nonatomic, retain) CEPeoplePickerNavigationController *ppnc;
#end
#protocol CEPeoplePickerNavigationControllerDelegate <NSObject>
- (void)cePeoplePickerNavigationController:(CEPeoplePickerNavigationController *)peoplePicker didFinishPickingPeople:(NSArray*)people values:(NSDictionary *)values;
#end
when the user presses submit button,Im executing the following code,
if ([self.ppnc.peoplePickerDelegate respondsToSelector:#selector(cePeoplePickerNavigationController:didFinishPickingPeople:values:)])
[self.ppnc.peoplePickerDelegate cePeoplePickerNavigationController:self.ppnc didFinishPickingPeople:sortedPeople values:[NSDictionary dictionaryWithDictionary:self.selectedValues]];
But the data is not being passed back to the previous view controller. Why so?
UPDATE
i tried the following code to move form first to second view controller,
CEPeoplePickerNavigationController *nextVC = [self.storyboard instantiateViewControllerWithIdentifier:#"PeoplePickerNavigationController"];
nextVC.peoplePickerDelegate = self;
[self presentViewController:nextVC animated:YES completion:nil];
but it throws the following error,
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UINavigationController setPeoplePickerDelegate:]: unrecognized selector sent to instance 0x101054800'
You just need to pass data using Custom Delegate
Write Below code on your CEPeoplePickerNavigationController's .h file
#class CEPeoplePickerNavigationController;
#protocol CEPeoplePickerNavigationController <NSObject>
- (void)previousViewController:(CEPeoplePickerNavigationController *)controller itemToSend:(NSString *)item;
#end
#interface CEPeoplePickerNavigationController : UIViewController
#property (nonatomic, weak) id < CEPeoplePickerNavigationController Delegate> delegate;
#end
on your moving to previous view controller event you need to put below code
[self.delegate previousViewController:self itemToSend:#"From Previous VC"];
[self.navigationController popViewControllerAnimated:true];
On your CallViewViewController you just need to add below code on .m file
#import "CEPeoplePickerNavigationController.h"
#interface ViewController ()< CEPeoplePickerNavigationController Delegate>
Add method declare on previous view controller
- (void)previousViewController:(CEPeoplePickerNavigationController *)controller itemToSend:(NSString *)item
{
NSLog(#"from CEPeoplePickerNavigationController %#",item);
}
make sure when you navigate to CEPeoplePickerNavigationController you need to assign delegate to self as below
CEPeoplePickerNavigationController *objVC = [self.storyboard instantiateViewControllerWithIdentifier:#"CEPeoplePickerNavigationController"];
objVC.delegate = self;
[self.navigationController pushViewController:infoController animated:YES];
When you are moving from CallViewViewController to CEPeoplePickerNavigationController , there must be some sort of navigation where you are pushing or applying segue to go the next VC.
Just add this line of code there -
If you are using segue --
CEPeoplePickerNavigationController *nextVC = [segue destinationViewController];
nextVC.peoplePickerDelegate = self;
If you are using instantiate --
CEPeoplePickerNavigationController *nextVC = [self.storyboard instantiateWithStoryboardid:#"YOUR STORYBOARD ID"];
nextVC.peoplePickerDelegate = self;
Once this is done, it will respond to the delegate from the next controller
EDIT
Please clear your CEPeoplePickerNavigationController .h code to the following
#protocol CEPeoplePickerNavigationControllerDelegate <NSObject>
- (void)cePeoplePickerNavigationController:(CEPeoplePickerNavigationController *)peoplePicker didFinishPickingPeople:(NSArray*)people values:(NSDictionary *)values;
#end
#interface CEPeoplePickerNavigationController : UIViewController <UITableViewDelegate,UITableViewDataSource>{
ABAddressBookRef addressBook;
}
#property (nonatomic, assign) id<CEPeoplePickerNavigationControllerDelegate> peoplePickerDelegate;
#property (nonatomic, retain) CEPeoplePickerNavigationController *ppnc;
#end
Related
I'm using a Split View Controller for an iPad app. I'm trying to send a label change to the detailReceiving Controller from the rootSending Controll when a button is pushed. I've read through tutorials on protocols and came up with the code below. When I click the button on rootSending, nothing happens to the label on detailReceiving. Do I have to do something else with a splitViewContoller so that the label will update? Shouldn't detailReceiving change the label when it receives the message?
rootSending.h
#import <UIKit/UIKit.h>
#protocol TestDelegate <NSObject>
-(void)tester:(NSString*)testString;
#end
#interface rootSending : UIViewController
#property (nonatomic, assign) id <TestDelegate> delegate;
#end
rootSending.m
#import "rootSending.h"
#implementation rootSending
#synthesize delegate;
-(void)viewDidLoad{
}
-(IBAction)buttonPressed:(id)sender{
[delegate tester:#"button pressed"];
}
#end
detailReceiving.m
#import "detailReceiving.h"
#import "rootSending.h"
#interface detailReceiving ()<TestDelegate>{
IBOutlet UILabel *label2;
}
#end
#implementation detailReceiving
-(void)viewDidLoad{
rootSending *obj = [rootSending alloc];
obj.delegate = self ;
}
-(void)tester:(NSString *)testString{
label2.text = testString;
}
#end
First of all, never ever have an alloc without an init! But in this case, even if you did use alloc/init, it still wouldn't work because that just creates a new instance of rootSending, not the one that you have in your split view. You need to get a reference to the one you have, which you can get from the split view controller,
-(void)viewDidLoad{
rootSending *obj = (rootSending *)self.splitViewController.viewControllers.firstObject;
obj.delegate = self;
}
After Edit:
If your mate controller is embedded in a navigation controller, then you need to get the navigation controller's topViewController to get your reference.
-(void)viewDidLoad{
UINavigationController *nav = (UINavigationController *)self.splitViewController.viewControllers.firstObject;
xmlListOfItems *obj = (xmlListOfItems *)nav.topViewController;
obj.delegate = self;
}
I have a tableViewCell that says Nickname and when you click on it, a segue takes you to a view controller with a text field where you can type a nickname, once you push the back button it should appear in the detailTextLabel of the main table view controller. I just keep getting an error I posted the code below where I get an error as well.
nickname text field code:
#import "NicknameViewController.h"
#interface NicknameViewController ()
#end
#implementation NicknameViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self.delegate updateCarNickname:self.nicknameField];
}
#end
Here is the header file for the nickname view controller:
#import <UIKit/UIKit.h>
#protocol CarNicknameDelegate <NSObject>
#optional
- (void)updateCarNickname:(UITextField *)updateNickname;
#end
#interface NicknameViewController : UIViewController
#property (nonatomic, weak) id <CarNicknameDelegate> delegate;
#property (strong, nonatomic) IBOutlet UITextField *nicknameField;
#end
In the main view controller I keep getting errors on this code and can't seem to figure it out:
-(void)updateCarNickname:(UITextField *)updateNickname {
self.nicknameCell.detailTextLabel.text = updateNickname;
}
Before the segue begins I used this code:
if ([segue.identifier isEqualToString:#"nicknameSegue"]) {
NicknameViewController *controller = segue.destinationViewController;
controller.delegate = self;
}
All views in a view controller gets deallocated once the view controller's dealloc is called.
You should change your CarNicknameDelegate's parameter UITextField to NSString.
#protocol CarNicknameDelegate <NSObject>
#optional
- (void)updateCarNickname:(UITextField *)updateNickname;
#end
and change your NicknameViewController to something like this.
#interface NicknameViewController ()
#end
#implementation NicknameViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self.delegate updateCarNickname:self.nicknameField.text];
}
#end
I know there are tons of questions out there about passing messages between different view controllers. I've checked them all but I can't get it working.
I've followed this tutorial: http://www.youtube.com/watch?v=XZWT0IV8FrI replacing the storyboard with a navigation controller but I run across the following issue over and over again: 'Cannot find protocol declaration for...'
Here is the code:
FirstViewController.h
#import "SecondViewController.h"
#interface FirstViewController : UIViewController <SecondViewControllerDelegate>{
//In this line above is where I get the error 'Cannot find protocol declaration for SecondViewControllerDelegate'
IBOutlet UITextField *userNameTextField;
}
#property (nonatomic, strong) UITextField *userNameTextField;
-(IBAction)goNext:(id)sender;
#end
FirstViewController.m
#import "FirstViewController.h"
#interface FirstViewController ()
#end
#implementation FirstViewController
#synthesize userNameTextField;
-(void)goNext:(id)sender{
SecondViewController *secondVC = [[SecondViewController alloc]init];
secondVC.delegate = self;
[self.navigationController pushViewController:secondVC animated:YES];
}
-(void)done:(NSString*)name{
NSLog(#"BACK in firstVC");
userNameTextField.text = name;
}
#end
SecondViewController.h
#import "FirstViewController.h"
#protocol SecondViewControllerDelegate <NSObject>
-(void)done:(NSString*)someText;
#end
#interface SecondViewController : UIViewController{
IBOutlet UITextField *someText;
IBOutlet UIButton *returnButton;
id delegate;
}
#property (assign, nonatomic) id <SecondViewControllerDelegate> delegate;
#property (strong, nonatomic) UITextField *someText;
-(IBAction)goBack:(id)sender;
#end
SecondViewController.m
#import "SecondViewController.h"
#interface SecondViewController ()
#end
#implementation SecondViewController
#synthesize someText;
#synthesize delegate = _delegate;
-(void)goBack:(id)sender{
[self.delegate done:someText.text];
FirstViewController *firstVC = [[FirstViewController alloc]init];
[self.navigationController pushViewController:firstVC animated:YES];
}
#end
In your SecondViewController goBack implementation you are creating a new FirstViewController rather than popping your navigation controller, code should read...
-(void)goBack:(id)sender{
[self.delegate done:someText.text];
[self.navigationController popViewControllerAnimated:YES];
}
And also in your SecondViewController.h remover this #import "FirstViewController.h" as it is no longer needed and could be confusing the compiler
Your protocol name is EYSSecondViewControllerDelegate:
#protocol EYSSecondViewControllerDelegate <NSObject>
but you call it SecondViewControllerDelegate in two places:
#interface EYSFirstViewController : UIViewController <SecondViewControllerDelegate>{...
#property (assign, nonatomic) id <SecondViewControllerDelegate> delegate;...
Make sure that the name match and it should works fine.
In SecondViewController.h
remove line id delegate;
In SecondViewController.m
Update code -> [delegate done:someText.text];
'self.' remove
Try it
I have an initial ViewController, lets call it HomeViewController, that has a button which calls a modal view controller, lets call it ModalViewController. Inside that ModalViewController I have a table view with two sections. If you click on any cell from section 0 it sends information back to HomeViewController (this part I have working with Protocol). If you click on any of the cells from section 1 it pushes to another view controller with options, lets call it OptionsViewController. Here is where it gets tricky for me. If you click any of those options, dismiss OptionsViewController and close ModalViewcontroller and send that information to HomeViewController, just like ModalViewController to HomeViewController. I have tried to set up a protocol similar to the one in ModalViewController but it is never called.
OptionsViewController protocol & .h file
#protocol OptionsViewControllerDelegate <NSObject>
#optional
-(void) optionsInfo:(NSArray *)optionsViewArray;
#end
#interface OptionsViewController : UITableViewController
#property (retain) id delegate;
#property (nonatomic, strong) NSArray *sendArray;
#end
OptionsViewController.m where it's called to pop off the stack.
{
[self dismissOptionsView];
}
-(void) viewWillDisappear:(BOOL)animated
{
NSLog(#"Send Array: %#", self.sendArray);
[[self delegate] optionsInfo:sendArray];
}
-(void)dismissOptionsView
{
[self.navigationController popViewControllerAnimated:YES];
}
Inside ModalViewController.h
#protocol ModalViewControllerDelegate <NSObject>
#optional
-(void) sendInformation:(NSArray *)infoArray;
#end
#interface ModalViewController : UITableViewController <ConditionsViewControllerDelegate, UISearchBarDelegate>
{
UISearchBar *searchDrugBar;
}
#property (retain) id delegate;
#property (nonatomic, strong) IBOutlet UISearchBar *searchDrugBar;
#property (nonatomic, strong) NSArray *infoArray;
#end
ModalViewController.m where OptionsInfo is supposed to come in.
-(void) optionsInfo:(NSArray *)optionsViewArray
{
//IT NEVER REACHES HERE :(
NSLog(#"HERE");
self.infoArray = optionsViewArray;
NSLog(#"Finished");
[self dismissViewControllerAnimated:YES completion:nil];
}
Has any one has done something similar like this or knows the solution to this? Any information, link, guidance and etc. to the right direction will be appreciated. Thanks in advance.
You need to set the delegate in your OptionsViewController below:-
In your OptionsViewController.m Include below line on your method
[self setDelegate:ModalViewController];
In UINavigationController this is child controller
.h
#protocol childProtocol <NSObject>
-(void)childMethod:(NSArray*)params;
#end
#property (strong, nonatomic) id<childProtocol>childDelegate;
#property (weak, nonatomic) parentVC *pVC;
.m
if([self.childDelegate respondsToSelector:#selector(childMethod:)]) {
[self.childDelegate performSelector:#selector(childMethod:) withObject:self.arry];
}
This is my parent controller
.m
-(void)childMethod:(NSArray *)params {
// some work
}
...
childVC *cVC = [[childVC alloc]init];
cVC.pVC = self;
But childMethod: is not getting called so I searched on internet and got this post
UINavigationControllers: How to pass value to higher (parent?) controller in stack?
I tried to create a weak reference but dont know how to use to make delegate pass data from child to parent?
Try this. Check the sample project attached
ParentViewController.h
#import <UIKit/UIKit.h>
#interface ParentViewController : UIViewController
- (void)passData:(NSString *)strText;
#end
ParentViewController.m
- (IBAction)btnGoToSecondView:(id)sender {
ChildViewController *secondVC = [[ChildViewController alloc] initWithNibName:#"ChildViewController" bundle:nil];
secondVC.delegate = self;
[self presentViewController:secondVC animated:YES completion:nil];
}
- (void)passData:(NSString *)strText {
NSLog(#"Data Passed = %#",strText);
}
ChildViewController.h
#import <UIKit/UIKit.h>
#import "ParentViewController.h"
#class ParentViewController;
#interface ChildViewController : UIViewController
#property(nonatomic, assign) ParentViewController *delegate;
#end
ChildViewController.m
- (IBAction)btnPassDataBack:(id)sender {
if([self.delegate respondsToSelector:#selector(passData:)]) {
[self.delegate passData:#"Hello"];
}
[self dismissViewControllerAnimated:YES completion:nil];
}
Sample Project
This is child controller.h
#protocol childProtocol <NSObject>
-(void)childMethod:(NSArray*)params;
#end
#property (strong, nonatomic) id<childProtocol>childDelegate;
#property (weak, nonatomic) parentVC *pVC;
.m
if([self.childDelegate respondsToSelector:#selector(childMethod:)]) {
[self.childDelegate performSelector:#selector(childMethod:) withObject:self.arry];
}
This is my parent controller.h
#import <UIKit/UIKit.h>
#import "ChildController.h"
#interface perentController : UIViewController < childProtocol >
.m
- (void)childMethod:(NSArray *)params {
// some work
}
EDITED :
And Dont Forget to add childViewOBJ.childDelegate = self; at the time of create ChildViewController's object. such like,
childVC *cVC = [[childVC alloc]init];
cVC.childDelegate = self;
cVC.pVC = self;
[self presentModalViewController:cVC animated:YES];
For More information about How to create/use of Protocol.
First of all, you are not checking for the same selector as you declared in your protocol declaration so it won't respond to that. You declared the method childMethod: whereas you are checking if your childDelegate responds to myMethod: selector which does not so it won't go into the if condition.
Also the parent view controller is missing the implementation the method childMethod: in its .m. Implement that in your parent view controller or it will crash because of not finding the exact selector definition.
Since you are using a UINavigationController, the parent view controller won't be lost till the child view controller exist so the childDelegate property must not be strong unless you intend to hold onto your delegate in child view controller for some reason.