I am trying to make a custom protocol that i hope somebody might help with.
I have a mainViewController (mainVC) that has a label. That label needs to be updated with a string when i press a button in edwwVC.
I am using ARC and storyboard.
The problem is when i press the Done Button on the edwwVC, the "done" method is called BUT the delegate method is not called in mainVC.
Whereas, if i call the done method VIA the mainVC, then the done method is called AND the delegate method. So I can see the connection is there, I just do not understand why the delegate method is not called when i press the done button in the edwwVC.
I imagine it has something to do with the init of the edwwVC. Because it is already initiated by storyboard, so it looks to me as if I am initializing it again the in the viewDidLoad method of the mainVC. But that is how far i got :)
Thanks in advance!
edwwVC.h
#import <UIKit/UIKit.h>
#import "IIViewDeckController.h"
#class EDWWViewController;
#protocol EDWWViewControllerDelegate <NSObject>;
#optional
- (void)edwwVCDidFinish:(EDWWViewController *)edwwVC;
#end
#interface EDWWViewController : UIViewController <IIViewDeckControllerDelegate> {
__weak id<EDWWViewControllerDelegate> delegate;
NSMutableArray *edwwPoints;
}
#property (weak) id<EDWWViewControllerDelegate> delegate;
#property (weak, nonatomic) IBOutlet UITableView *theTableView;
#property (strong, nonatomic) NSString *testString;
- (IBAction)done:(id)sender;
- (IBAction)add:(id)sender;
#end
edwwVC.m:
#pragma mark - delegate method
- (IBAction)done:(id)sender {
testString = #"This is the test string!";
[delegate edwwVCDidFinish:self];
[self.viewDeckController closeRightViewAnimated:YES];
NSLog(#"Done pressed");
}
MainVC.m:
- (void)viewDidLoad
{
[super viewDidLoad];
edwwViewController = [[EDWWViewController alloc] init];
edwwViewController.delegate = self;
}
- (void)edwwVCDidFinish:(EDWWViewController *)edwwVC {
edwwLabel.text= edwwVC.testString;
NSLog(#"delegate method called");
}
Remove the line ...
__weak id<EDWWViewControllerDelegate> delegate;
From the .h and change the line...
[delegate edwwVCDidFinish:self];
to...
[self.delegate edwwVCDidFinish:self];
In the .m.
That should sort it.
The way you have it set up the ivar delegate is not the same as the property delegate (which is actually an ivar called _delegate) (thanks #Joris Kluivers, just adding for clarity). They are pointing to different things.
If you add a breakpoint where you are calling the delegate method I think you'll find that delegate is nil. Whereas _delegate (or self.delegate) is not nil.
::EDIT::
Ahh... just spotted the second bit too.
If you are setting up the edwwvc in storyboard then you should be alloc initing it too.
If you are segue-ing to the edwwvc then you should intercept the segue in mainVC.m like this...
- (void)prepareForSegue: //blah the rest of the name...
{
if ([segue.identifier isEqualToString:#"the name of your segue"])
{
EDWWViewController *controller = segue.destinationViewController;
controller.delegate = self;
}
}
This will take the controller that you are pushing to from the storyboard and set the delegate to it.
:: ANOTHER EDIT ::
If EDWWVC is inside a containerViewController then you can do this inside viewDidLoad in MainVC.m...
- (void)viewDidLoad
{
// other stuff...
for (UIViewController *controller in self.childViewControllers) {
if ([controller isKindOfClass:[EDWWViewController class]]) {
EDWWViewController *edwwvc = (EDWWViewController*)controller;
eddwvc.delegate = self;
}
}
}
You may find this code has to go in viewDidAppear or something but I think viewDidLoad shouldd work just fine.
You may actually be able to set the delegate property directly by using the storyboard to (but I'm not 100% certain on this).
The answer was in the containerVC of both controllers.
Where i initialized the view controllers: the viewDidLoad of the containerVC m file:
- (void)viewDidLoad
{
[super viewDidLoad];
mainVC = (MainViewController *)[self.storyboard instantiateViewControllerWithIdentifier:#"MainVC"];
edwwVC = (EDWWViewController *)[self.storyboard instantiateViewControllerWithIdentifier:#"EDWWVC"];
//THIS LINE WAS MISSING
edwwVC.delegate = mainVC;
self.centerController = mainVC;
self.rightController = edwwVC;
}
BUT guys thanks for the help! :) Appreciate it got me in the right direction! :) THANKS! :)
Related
Look at the following code.
In CusFormViewController.h
#interface CusFormViewController : CusBaseViewController
#protocol CusFormViewControllerDelegate <NSObject>
-(void)ticketCreated:(NSString*)ticketID;
-(void)ticketFormRenderingFinished;
#end
#property (nonatomic, weak) id<CusFormViewControllerDelegate> delegate;
In CusFormViewController.m
if ([self.delegate respondsToSelector:#selector(ticketFormRenderingFinished)])
[self.delegate ticketFormRenderingFinished];
if ([self.delegate respondsToSelector:#selector(ticketCreated:)])
[self.delegate ticketCreated:ticket_id];
In ViewController.m
#import "CusFormViewController.h"
#interface ViewController ()<CusFormViewControllerDelegate>
- (void)viewDidLoad {
[super viewDidLoad];
CusFormViewController *formVC = [[CusFormViewController alloc] init];
[formVC setDelegate:self];
}
-(void)ticketCreated:(NSString*)ticketID{
NSLog(#"ticket created.");
}
-(void)ticketFormRenderingFinished{
NSLog(#"ticket form rendered.");
}
The ticketCreated & ticketFormRenderingFinished are not being called.
Most common reason for delegate method not being called is dealing with incorrect objects.
Ensure that CusFormViewController object created from
ViewController is the same which you are presenting on screen and
that the delegate is being set on the same object. I truly believe
you are creating a new CusFormViewController object and setting
delegate on that.
In CusFormViewController, before calling delegate, check the
existence of delegate as well. This is a safety check, you can also put a
NSLog statement to double check if your delegate exists or not. You
are failing here.
If you are segueing from ViewController to CusFormViewController then you set delegate in prepareForSegue: and not in viewDidLoad.
As a side note, put a NSLog statement in viewDidLoad of your CusFormViewController and print self.delegate to check the property setting.
Your controller formVC is dealloced after the function viewDidLoad executed. Create strong reference on your formVC for example like this:
#interface ViewController ()<CusFormViewControllerDelegate>
{
CusFormViewController *_formVC;
}
- (void)viewDidLoad {
[super viewDidLoad];
_formVC = [[CusFormViewController alloc] init];
[formVC setDelegate:self];
}
Hey try to create instance of CusFormViewController using below method
CusFormViewController * _formVC=[[UIStoryboard storyboardWithName:storyboardName bundle: nil]instantiateViewControllerWithIdentifier:#"IDENTIFIER_OF_YOUR_ CusFormViewController"];
I am quite new to IOS, and I was working with delegates to call a method in parent ViewController.
ViewControllerRegistration.h
#protocol RegistrationViewControllerDelegate;
#interface ViewControllerRegistration : UIViewController
#property (nonatomic, weak) id<RegistrationViewControllerDelegate> delegate;
- (IBAction)registerNewUser:(id)sender;
#end
// 3. Definition of the delegate's interface
#protocol RegistrationViewControllerDelegate <NSObject>
-(void)loginResult:(NSString*)loginData;
#end
ViewControllerRegistration.m
- (void)registerNewUser:(id)sender {
id<RegistrationViewControllerDelegate> strongDelegate = self.delegate;
// Our delegate method is optional, so we should
// check that the delegate implements it
if ([strongDelegate respondsToSelector:#selector(loginResult:)]) {
[strongDelegate loginResult: #"#WHY YOU NOT BEING CALLED?"];
}
Parents:
ViewController.h
#import "ViewControllerRegistration.h"
#interface ViewController : UIViewController <GPPSignInDelegate,FBSDKLoginButtonDelegate,RegistrationViewControllerDelegate>
#end
ViewController.m
#implementation ViewController
#synthesize signInButton;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
ViewControllerRegistration *detailViewController = [[ViewControllerRegistration alloc] init];
// Assign self as the delegate for the child view controller
detailViewController.delegate = self;
}
//Local Strategy
- (IBAction)navigateToLocalSignUpNavBtn:(id)sender {
[self performSegueWithIdentifier:#"SegueRegisterUserAction" sender:self];
}
// Implement the delegate methods for RegistrationViewControllerDelegate
-(void)loginResult:(NSString*)loginData{
NSLog(#"I am called");
//^Above is never printed. That means delegate method is never called
}
Please note, my parent view controller is embedded in a navigation controller.
The delegate method is never called. Tried debugging, but in vain.
The view controller you are creating in viewDidLoad isn't the same one that you will be segueing to. It's another instance that will be deallocated at the end of the method. You need to access the correct instance in prepareForSegue and set the delegate there.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"SegueRegistrationUserAction"]) {
[(ViewControllerRegistration *)segue.destinationViewController setDelegate:self];
}
}
Yesterday, I had explained this in length on this thread. Please take a look and let me know if you have any specific question post that. I hope this would help you.
i'm trying to create my first delegate here's what i'm trying to do
at class called PAStepper.h i did this
#class PAStepper;
#protocol StepperDelegate <NSObject>
#required
-(void)didIncrement;
-(void)didDecrement;
#end
#interface PAStepper : UIControl
{
__weak id <StepperDelegate> stepperDelegate;
}
#property (nonatomic, weak) id <StepperDelegate> stepperDelegate;
#end
and synthized the property of course in the .m file
and then putten in the PAStepper.m the following in a method
[self.stepperDelegate didDecrement];
in the controller i want to take the delegate i did this
.h
#interface OCSideCartViewController : UIViewController<StepperDelegate>
.m
- (void)viewDidLoad
{
[super viewDidLoad];
PAStepper *stepper = [[PAStepper alloc]init];
stepper.stepperDelegate = self;
}
-(void)didIncrement{
NSLog(#"inc");
}
-(void)didDecrement{
NSLog(#"dec");
}
knowing that this class is viewController that contain a table that every cell have PAStepper .. all i wanted that the delegate call didIncrement and didDecrement whenever i press the buttons
When you call this method
[self.stepperDelegate didDecrement];
NSLog you stepperDelegate and ensure it is not null. Can you confirm the method which calls your delegate is being called?
you can call this in your pastapper.h file
if ([stepperDelegate respondsToSelector:#selector(didIncrement)])
{
[stepperDelegate performSelector:#selector(didIncrement)];
}
I can´t download the project, so i am guessing..
Is the Stepper in the Interface Builder?
If yes you don´t have to allocate it.
Make an Outlet Connection of the Stepper in the OCSideCartViewController.
Post this in OCSideCartViewController:
- (void)viewWillAppear:(BOOL)animated{
self.stepper.stepperDelegate = self;
[super viewWillAppear:animated];
}
If the Stepper is not an Outlet, you have to add it to your ViewControllers view after setting it´s bounds..
I ultimately want to write an iOS app incorporating ALAssetsLibrary, but as a first step toward understanding delegation, I'm trying to pass a simple message between two view controllers. For some reason, I can't seem to get the message to pass. In particular, the delegate object (derpy) doesn't appear to exist (if(self.derpy) returns NO)).
I asked the same question on the Apple forums and was told that I should be using segues and setting properties / calling methods using self.child instead, but that seems strange. If I were to pass messages using the parent / child properties, would I still be able to create my views in Interface Builder? Once I have my two views set up, say inside a UINavigationController, I'm not sure how to actually "wire them up" so I can pass messages between them. Sorry if the question is overly broad.
Here's the controller I'm declaring the protocol in (called PickerViewController):
Interface:
#import <UIKit/UIKit.h>
#import <AssetsLibrary/AssetsLibrary.h>
#protocol DerpDelegate <NSObject>
#required
- (void) test;
#end
#interface PickerViewController : UIViewController
#property (nonatomic, assign) id<DerpDelegate> derpy;
#end
Implementation:
#import "PickerViewController.h"
#interface PickerViewController ()
#end
#implementation PickerViewController
- (void)viewDidLoad
{
[super viewDidLoad];
if (self.derpy) { // If the delegate object exists
[self.derpy test]; // send it this message
} else {
NSLog(#"Still not working."); // This always returns (i.e., self.derpy doesn't exist)
}
}
Delegate controller (MainViewController) interface:
#import <UIKit/UIKit.h>
#import "PickerViewController.h"
#interface MainViewController : UIViewController <DerpDelegate> // public promise to implement delegate methods
#property (strong, nonatomic) PickerViewController *picker;
- (void) test;
#end
And lastly, the delegate controller (MainViewController) implementation:
#import "MainViewController.h"
#import "PickerViewController.h"
#interface MainViewController ()
#end
#implementation MainViewController
// Here's that method I promised I'd implement
- (void) test{
NSLog(#"Test worked."); // This never gets called
}
- (void)viewDidLoad {
[super viewDidLoad];
self.picker.derpy = self;
//lazy instantiation
- (PickerViewController *) picker{
if(!_picker) _picker = [[PickerViewController alloc]init];
return _picker;
}
EDIT: Many thanks to rydgaze for pointing me in the right direction with self.picker.derpy = self, but for some reason, things still aren't working properly. Importantly, once that property has been set, if(self.picker.derpy) returns YES from MainViewController. But if(self.derpy) is still returning NO when called from inside the PickerViewController's viewDidLoad. How can the property exist and not exist at the same time?
You need to be sure that you're setting the delegate on the instance of the view controller that you put on screen. If you're using a navigation controller and segues to go between MainViewController and PickerViewController, then you should set the delegate in prepareForSegue:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
self.picker = (PickerViewController *)segue.destinationViewController;
self.picker.derpy = self;
}
You need to populate the delegate first.
Basically, your MainViewController shoudl at somepoint do a
picker.derpy = self;
Then when the delegate fires in PickerViewController, the callback will happen.
Edit:
A good practice is to do something like in PickerViewController
#property (nonatomic, assign) id<DerpDelegate > derpy;
and in your MainViewController indicate that you will implement the delegate
#interface MainViewController : UIViewController<DerpDelegate>
Eventually in your implementation of MainViewController
You will have something like
picker = [[PickerViewController alloc]init];
picker.derpy = self;
[picker doYourThing];
Once picker is all done, it may want to return results using the delegate.
I have been searching all morning how to do this. I have 2 View Controllers. From the root View Controller (ViewControllerA - which is a table view controller) you can go push to the second view controller (ViewControllerB).
In the ViewControllerB, there are two fields: contacts & textBody. When the user is done they can click on "Add". This will then go back to ViewControllerA. What I am trying to do now, is for every time that process occurs, all the information from ViewControllerB the user just added goes into a cell in ViewControllerA. The user can then add as many cells as they like.
What I can't do however, is get the information across the view controllers. I have been looking all morning at using the app delegate, singletons??, protocols, sharing properties, etc! But I am still stuck.
What I want to do, but can't, is for every time the user clicks "Add" on ViewControllerB, contacts & texts are put into an array. This array is then put into another array which holds all the smaller arrays which the user has created? If you have an ideas, or links to similar/sample code or tutorials, that would be much appreciated!
Try this using the delegate method as follows
Download Sample Project with XIBs
Download Sample Project With Storyboard
ParentViewController.h
#import <UIKit/UIKit.h>
#interface ParentViewController : UIViewController {
NSMutableArray *dataArray;
}
- (void)passData:(NSMutableArray *)array;
#end
ParentViewController.m
#import "ParentViewController.h"
#import "ChildViewController.h"
#implementation ParentViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Initialise the mutable array.
dataArray = [[NSMutableArray alloc] init];
}
- (IBAction)btnGoToSecondView:(id)sender {
ChildViewController *secondVC = [[ChildViewController alloc] initWithNibName:#"ChildViewController" bundle:nil];
secondVC.delegate = self;
[self presentViewController:secondVC animated:YES completion:nil];
}
- (void)passData:(NSMutableArray *)array {
[dataArray addObject:array];
NSLog(#"Data Passed = %#",dataArray);
}
#end
ChildViewController.h
#import <UIKit/UIKit.h>
#import "ParentViewController.h"
#class ParentViewController;
#interface ChildViewController : UIViewController {
NSMutableArray *tempArray;
}
#property (strong, nonatomic) IBOutlet UITextField *txtContact;
#property (strong, nonatomic) IBOutlet UITextField *txtTextBody;
#property(nonatomic, assign) ParentViewController *delegate;
#end
ChildViewController.m
#implementation ChildViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Initialise the mutable array.
tempArray = [[NSMutableArray alloc] init];
}
- (IBAction)btnPassDataBack:(id)sender {
if([self.delegate respondsToSelector:#selector(passData:)]) {
[tempArray addObject:_txtContact.text];
[tempArray addObject:_txtTextBody.text];
[self.delegate passData:tempArray];
}
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)viewDidUnload {
[self setTxtContact:nil];
[self setTxtTextBody:nil];
[super viewDidUnload];
}
#end
With Storyboard
If you are using storyboard then create a ParentViewController segue ChildViewController and give it a identifier in my sample it showChildView
Then use the following code to set the delegate
// Calling the segue to go to the child view and setting up the delegate.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showChildView"]) {
ChildViewController *childVC = segue.destinationViewController;
childVC.delegate = self;
}
}
Then to dismiss back to the ParentViewController use the following code (from my sample)
- (IBAction)btnPassDataBack:(id)sender {
if([self.delegate respondsToSelector:#selector(passData:)]) {
[tempArray addObject:_txtContact.text];
[tempArray addObject:_txtTextBody.text];
[self.delegate passData:tempArray];
}
[self.navigationController popToRootViewControllerAnimated:YES];
}
I would recommend using a singleton instance of your NSMutableDictionary as they have bailed me out of your exact situation multiple times (including custom frameworks and UITabBarControllers). Here is an example I'm currently using to implement a singleton. This methodology is also ARC-safe as well
mySingleton.h
#import <Foundation/Foundation.h>
#interface mySingleton : NSObject {
}
+ (NSMutableDictionary *) myMutableDict;
#end
mySingleton.m
#import "mySingleton.h"
#implementation mySingleton
+ (NSMutableDictionary *)myMutableDict
{
static NSMutableDictionary *singletonInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singletonInstance = [[NSMutableDictionary alloc]init];
});
return singletonInstance;
}
#end
As long as you include mySingleton.h in all of your view controllers you can access the data via [mySingleton myMutableDict]. For example: [[mySingleton myMutableDict] setObject:myObject forKey:myKey];
Good luck!
If the information is really "global" - it has only one instance across the whole app - then you should create a singleton as DB80Buckeye suggested.
If the information is something that truly belongs to ViewController1 and you want it to be modified in ViewController2 (ie ViewController2 is really part of ViewController1, it just happens to be on another screen), then you should pass that as part of the constructor of ViewController2.
-(void)view_controller_1_that_push_view_controller_2_onto_the_stack {
ViewController2* vc2 = [[ViewController2 alloc] initWithInformation:your_information];
[self.navigationController pushViewController:vc2 animated:YES];
}
#interface ViewController2
-(id)initWithInformation:(YourInformationClass*)info;
#end
Another way is to use notifications.
There are two ways to go here. The standard pattern for doing this is delegation. You don't need a singleton. ViewControllerA manages and lists your data. ViewControllerB doesn't need to know anything about all of that data so there's no reason to expose it via a singleton, etc.
Create a delegate protocol in ViewControllerB's header file. Something like this:
#protocol ViewControllerBDelegate
- (void)addContact:(NSString *)contact withBody:(NSString *)textBody;
#end
Now, specify that ViewControllerA will implement the delegate protocol in its header:
#interface ViewControllerA : UIViewController <ViewControllerBDelegate>
Don't forget to import ViewControllerB.h at the top of ViewControllerA's header.
In ViewControllerA's implementation, implement the delegate method you specified in the protocol:
- (void)addContact:(NSString *)contact withBody:(NSString *)textBody {
[self.someArray addObject:[[SomeObject alloc] initWithContact:contact body:textBody]];
[self.tableView reloadData];
}
That's obviously just an example -- not sure how you're managing your data structure and it's probably better to insert the cell someplace that makes sense.
Declare a delegate reference in ViewControllerB's header:
#property (weak, nonatomic) id<ViewControllerBDelegate> delegate;
When you present ViewControllerB, set ViewControllerA as the delegate.
ViewControllerB *b = [[ViewControllerB alloc] init...];
b.delegate = self;
In the selector triggered by the add button in ViewControllerB, call back on the delegate before popping the view controller off the navigation stack:
[self.delegate addContact:contact withBody:text];
where contact and text are the values the user entered.
One could also use a block instead of a delegate but the principle is the same -- have the second view controller only be responsible for taking input, in your case, and pass it back to the view controller managing the data.
Alternatively for delegate suggest using the following:
ViewControllerA.h:
#property (nonatomic, strong) ViewControllerB* viewControllerB;
In ViewControllerA.m
if (!self.viewControllerB)
{
self.viewControllerB = [[ViewControllerB alloc] initWithNibName: #"ViewControllerBr" bundle: nil];
}
[self.navigationController pushViewController: self.viewControllerB
animated: YES];
...
- (void) viewWillAppear: (BOOL) animated
if (self.viewControllerB)
{
NSString* contact = self.viewControllerB.contact;
NSLog(#"%#", contact);
}
...