iOS Protocol - Delegate always nil - ios

I have two controllers. A BaseViewController and MyController. On BaseViewController I have as property an NSObject that has a Protocol
BaseViewController.h
#property (nonatomic, strong) MyListener *myListener;
MyListener.h
#protocol MyListenerProtocol;
#interface MyListener : NSObject
#property (nonatomic, weak) id<MyListenerProtocol> delegate;
#end
#protocol MyListenerProtocol;
#protocol MyListenerProtocol <NSObject>
#optional
-(void) myMethod: (int) input;
#end
MyController extends BaseViewController
#interface MyController : BaseViewController <MyListener>
and in it's viewDidLoad:
super.myListener = [[MyListener alloc] init];
super.myListener.delegate = self;
in BaseViewController in a method, when called it does:
if (self.myListener && [self.myListener.delegate respondsToSelector:#selector(myMethod:)]) {
[self.myListener.delegate myMethod: input];
}
But in this point the "self.myListener" is always nil.
What I want is to call a method in the child view controller when a BaseViewController's method is called (but only for some children view controllers, not all).
Any suggestions?

Please implement a custom getter for the property named delegate, set it normally and set a breakpoint there. This way you will find the cause of the problem.
Also, be aware that a weak property will go away as soon as no other objet points to the
Object that you set as delegate.

The most likely cause (and an insanely common cause) is that your BaseViewController method is being called prior to viewDidLoad. Have you verified this is not the case?
As a note, you mean self in all the places you've put super.
Also note, in this line:
if (self.myListener && [self.myListener.delegate respondsToSelector:#selector(myMethod:)]) {
There is no reason for self.myListener &&... If self.myListener is nil, the rest of the call will become false automatically.

You should tell the MyController the delegate it uses, but you tell the wrong name.
#interface MyController : BaseViewController <MyListener>
change to:
#interface MyController : BaseViewController <MyListenerProtocol>

Set delegate in prepareForSegue method.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
LastNameViewController *lastNameViewController = [segue destinationViewController];
lastNameViewController.delegate = self;
}

Related

Setting BOOL to "YES" in custom delegate

I've make a delegate so my two different view controllers can communicate and I'm stuck trying to set a BOOL to YES in my child view controller.
childViewController.h
#protocol pageTwoViewControllerDelegate;
#interface pageTwoViewController : UIViewController {
UIButton *takePhotoTransition;
}
#property (nonatomic, weak) id<pageTwoViewControllerDelegate> delegate;
#end
#protocol pageTwoViewControllerDelegate <NSObject>
- (BOOL)didPushTakePhoto;
#end
childViewController.m
...
- (IBAction)takePhotoTransition:(id)sender {
id<pageTwoViewControllerDelegate> strongDelegate = self.delegate;
if ([strongDelegate respondsToSelector:#selector(didPushTakePhoto)]) {
strongDelegate.didPushTakePhoto = YES; // ERROR: No setter method for 'setDidPushTakePhoto:' for assignment property
}
NSLog(#"Button push recieved");
}
How can I get past this error and set my BOOL to YES when my button is pushed?
The protocol is just telling everyone that knows about your class through the protocol, that the property anObject will be there. Protocols are not real, they have no variables or methods themselves
Try to modify your code to be like this, you are setting a non existence variable or property.
you have to implement new Class instead of id
your protocol will look like
#protocol pageTwoViewControllerDelegate <NSObject>
- (void)setdidPushTakePhoto:(BOOL)aBOOL;
- (BOOL)didPushTakePhoto;
#end
and your class.h will contain
#property (nonatomic, getter=get_didPushTakePhoto) BOOL didPushTakePhoto;
and your class.m will contain implementation
-(BOOL)didPushTakePhoto
{
return _didPushTakePhoto;
}
- (void)setdidPushTakePhoto:(BOOL)aBOOL{
_didPushTakePhoto=aBool;
}
You are getting confused between a method and a property.
The definition of your protocol "pageTwoViewControllerDelegate", says it should implement a method with name "didPushTakePhoto" which returns a BOOL value.
What you are trying to do is entirely different. You are trying to set a non-existent property. Whenever you are accessing something followed by a dot ".", that should be a property of the class to which that object belongs. The protocol you defined does not talk anything about the property.
So inside the if condition, you should be calling the method "didPushTakePhoto" on your delegate object, like below.
[strongDelegate performSelector:#selector(didPushTakePhoto)];
If you know for sure that your delegate implementations do have the protocol method implemented, then since you already cast self.delegate to strongDelegate which is declared as id, you don't need the if condition. You could directly call the method like below.
[strongDelegate didPushTakePhoto];
Hope this helps
Have you assigned the delegate of your ChildViewController belongs to ParentViewController?
Try this:
ParentChildViewController.m
#import "ChildViewController.h"
#interface ParentViewController () <ChildDelegate>
...
-(IBAction)btnClicked:(id)sender
{
ChildViewController *ctrl = [[ChildViewController alloc] init];
ctrl._delegate = self;
// do present childViewController or similar action here
}
- (void)didPushTakePhoto: (BOOL)result{
NSLog(#"result: %d",result);
}
ChildViewController.h
#protocol ChildDelegate
- (void)didPushTakePhoto: (BOOL)result;
#end
#interface pageTwoViewController : UIViewController {
UIButton *takePhotoTransition;
}
#property (assign, nonatomic) id _delegate;
#end
ChildViewController.m
...
- (IBAction)takePhotoTransition:(id)sender {
if ([self._delegate respondsToSelector:#selector(didPushTakePhoto:)]) {
[self._delegate didPushTakePhoto:YES];
// do dismiss here
}
}

Delegate not working in iOS

I feel frustrated with this problem, I my delegate doesn't work, Here's the code snippet below. I'm just using xib in this application.
//classA.h file
ClassA.h
#import "ClassB.h"
#interface ClassA : UIViewController< ClassBDelegate >
//classA.m file
**ClassA.m**
-(void)didSuccessPreview:(ClassB *)controller andLog:(NSString *)log{
NSLog(#"%#", log);
}
//classB.h file
**ClassB.h**
#import <UIKit/UIKit.h>
#class ClassB;
#protocol ClassBDelegate <NSObject>
- (void)didSuccessPreview:(ClassB *)controller andLog:(NSString *)log;
#end
#interface ClassB : UIViewController
#property (weak, nonatomic) id< ClassBDelegate >delegate;
//classB.m file
**ClassB.m**
I add this code below inside view did load
[self.delegate didSuccessPreview:self andLog:#"zz"];
I have other delegate inside my application same as the code above, but it works but this one is not, I don't know why. Inside my classA I have a button then when i click it it goes to classB, then when it goes to viewdidload in ClassB, the delegate doesn't fired.
Depending on where you assign the delegate, your code might need to look like this:
ClassB viewController = [ClassB new];
ClassA delegateStrongRef = [ClassA new]
viewController.delegate = delegateStrongRef; // <-- you are missing this now;
// Fixed: the ClassA object must be referenced strongly somewhere else
// besides the (weak)delegate property, otherwise it will be deallocated.
(The code above is the part where you create the instance of ClassB, the view controller. It might be inside the AppDelegate, another viewController's implementation, anything - depending on how your app is structured).
Then, in the view controller's -viewDidload method, you can have the delegate execute a method like this:
- (void)viewDidLoad
{
[self.delegate didSuccessPreview:self andLog:#"zz"];
}
so, at some point after instantiating your view controller, you need to assign an object of type ClassA (the class that adopts the delegate protocol) to its delegate property, otherwise it will be nil and any messages sent to it will be ignored (no method executed).
Properties and instance variables of object type are not allocated automatically, but initialized to nil (ints, floats etc, are initialized to 0, 0.0f, etc.)
EDIT: If you are assigning the delegate from ClassA's code, then it might look like this:
ClassA.m:
- (void) someMethodOfClassA
{
ClassB viewController = [ClassB new];
viewController.delegate = self;
// e.g., Do something with the view controller...
[navigationController pushViewController:viewController animated:YES];
}
Since the delegate property is defined as a weak reference, before calling any method of the delegate make sure it has not been deallocated and its reference set back to nil (use NSLog or set a breakpoint).

Delegate method not being called?

I have a view controller with a delegate method that should be called, but it doesn't?
NotifyingViewController.h
#protocol NotifyingViewControllerDelegate <NSObject>
#required
- (void)iWasAccepted;
#end
#interface NotifyingViewController : UIViewController
#property (nonatomic, weak) id<NotifyingViewControllerDelegate> delegate;
NotifyingViewController.m
-(void)someMethod{
[self.delegate iWasAccepted];
[self dismissViewControllerAnimated:YES completion:nil];
}
NotifiedViewController.h
#import "NotifyingViewController.h"
#interface NotifiedViewController : UIViewController <NotifyingViewControllerDelegate>
NotifiedViewController.m
-(void)iWasAccepted{
[self saveIntoDB];
NSLog(#"DELEGATE RAN");
}
For some reason, the controller that should be notified isn't. The Notifying controller does dismiss meaning the method that alerts the delegate IS run, but the delegate doesn't run the function because it doesn't NSLog. Any ideas why?
You can't just specify that an object conforms to a protocol. You must also assign that object as the delegate. When you alloc/init the instance of NotifyingViewController, set its delegate to self and you should be fine.
NotifyingViewController *notifyingInstance = [[NotifyingViewController alloc] init];
[notifyingInstance setDelegate:self];
It is important to both do this, and specify that the class conforms to the protocol, which you're already doing with this line.
#interface NotifiedViewController : UIViewController <NotifyingViewControllerDelegate>
Additionally, when calling delegate methods, it's good practice to wrap the function calls in respondsToSelector: checks.
if ([self.delegate respondsToSelector:#selector(iWasAccepted)]) {
[self.delegate iWasAccepted];
}

Why isn't my delegate object responding to method calls?

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.

Unable to set custom protocol delegate using ARC with two UITableViewControllers using UINavigationController

I'm trying to set the delegate for my custom protocol that has one required method allowing me to pass an array of objects back in the hierarchy of two UITableViewControllers. My delegate continues to return nil. Due to this, my required method is never called.
I'm wondering if the datasource and delegate implementations with my UITableViewControllers is causing a conflict. Also, perhaps ARC is getting in the way when declaring the delegate?
It should be noted that both UITableViewControllers were built using Storyboard and are navigated using segues within a UINavigationController (not sure if this may be causing issues or not).
The nav is --> AlarmViewController --> AlarmDetailsViewController. I create an Alarm object in my AlarmDetailsViewController that contains all the details for an alarm, place it into an array and I want to pass that array back to my AlarmViewController to be displayed in a custom cell in the table.
NOTE: I want to use the Delegate pattern here. I'm not interested in solutions that invoke NSNotifications or use my AppDelegate class.
AlarmDetailsViewController.h
#import "Alarm.h"
#protocol PassAlarmArray <NSObject>
#required
-(void) passAlarmsArray:(NSMutableArray *)theAlarmsArray;
#end
#interface AlarmDetailsViewController : UITableViewController <UITableViewDataSource, UITableViewDelegate>
{
//.....
id <PassAlarmArray> passAlarmsArrayDelegate;
}
#property (nonatomic, retain) id <PassAlarmArray> passAlarmsArrayDelegate;
#end
AlarmDetailsViewController.m
#import "AlarmDetailsViewController.h"
#interface AlarmDetailsViewController ()
#end
#implementation AlarmDetailsViewController
#synthesize passAlarmsArrayDelegate;
-(void) viewWillDisappear:(BOOL)animated
{
NSLog(#"delegate = %#", self.passAlarmsArrayDelegate); // This prints nil
[[self passAlarmsArrayDelegate] passAlarmsArray:alarmsArray];
}
//....
#end
AlarmViewController.h
#interface AlarmViewController : UITableViewController <UITableViewDataSource, UITableViewDelegate, PassAlarmArray>
{
//...
AlarmDetailsViewController *alarmDetailsViewController;
}
#property (nonatomic, retain) AlarmDetailsViewController *alarmDetailsViewController;
#end
AlarmViewController.m
#import "AlarmViewController.h"
#import "AlarmDetailsViewController.h"
#import "AlarmTableViewCell.h"
#import "Alarm.h"
#interface AlarmViewController ()
#end
#implementation AlarmViewController
#synthesize alarmDetailsViewController;
- (void)viewDidLoad
{
[super viewDidLoad];
// This is where I'm attempting to set the delegate
alarmDetailsViewController = [[AlarmDetailsViewController alloc]init];
[alarmDetailsViewController setPassAlarmsArrayDelegate:self];
}
//....
//My #required protocol method which never gets called since my delegate is nil
-(void) passAlarmsArray:(NSMutableArray *)theAlarmsArray
{
alarmsTableArray = theAlarmsArray;
NSLog(#"alarmsTableArray contains: %#", alarmsTableArray); // Never gets called due to delegate being nil
NSLog(#"theAlarmsArray contains: %#", theAlarmsArray); // Never gets called due to delegate being nil
}
#end
I've attempted to set the delegate in a method that fires when a button is pressed in AlarmViewController (as opposed to the viewDidLoad method) but that does not work either.
I'm assuming I've got a logic flow error somewhere here . . . but nearly 2 days of hunting and rebuilds haven't uncovered it. Ugh.
You're setting your delegate in the wrong place, and on a different instance of the controller than the one you will get when you do the segue. You should set the delegate in the prepareForSegue method if you're pushing AlarmDetailsViewController from AlarmViewController
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
AlarmDetailsViewController *alarm = segue.destinationViewController;
alarm.passAlarmsArrayDelegate = self;
}
You really need to understand the life cycle of view controllers, how and when they're instantiated, and when they go away. This is the very heart of iOS programming, and Apple has extensive documentation on it. Reading up on segues would also be very useful. A segue (other then an unwind segue) always instantiates a new instance of the destination controller. So, when your segue is performed, whether directly from a button, or in code, a new (different from the one you alloc init'd directly) details controller is instantiated. Before that segue is performed, prepareForSegue: is called, and that's when you have access to the one about to be created. That's the place to set a delegate or pass any information on to the destination view controller.
Did you try replace (nonatomic, retain) with (nonatomic, strong) since you are using ARC?
Auto-synthesized properties like your alarmDetailsViewController property have backing ivars prefixed with underscores, e.g. _alarmDetailsViewController. Your alarmDetailsViewController ivar (the alarmDetailsViewController declared inside the #interface ... {} block in AlarmViewController.h) is different from the backing ivar of your alarmDetailsViewController property.
Just delete your alarmDetailsViewController ivar and use the #property, preferably through self.alarmDetailsViewController.

Resources