Delegate changes unexpectedly - ios

I have one delegate ExampleDelegate and I have one UITableViewController and detail UIViewController both implementing that delegate:
#interface ClassA : UITableViewController <ExampleDelegate>
and:
#interface ClassB : UIViewController <ExampleDelegate>
and delegate:
#protocol ExampleDelegate <NSObject>
-(void)notifyUser;
#end
#interface Example : NSObject
#property (nonatomic, retain) id delegate;
-(id)initWithDelegate:(id<ExampleDelegate>) delegate;
#end
At first I'm initializing the instance of delegate from ClassA and its working fine but once I navigate to the ClassB there also I'm creating one instance for the delegate when I came back to the ClassA the delegate instance get retained from ClassB, so the function inside the ClassB gets called always instead of ClassA's function.
Can anyone point me out what I'm doing wrong and how to get this working?

You need to make sure that you nil out your delegates once they're not needed - this will help you achieve 2 things:
Pass the correct class the proper delegation that it needs to handle when it is visible.
Prevent crashes so that when a class gets deallocated and is defined as a delegate, you might get a "message sent to deallocated instance" crash.
In your case, once ClassB goes back to ClassA, you must make sure that ClassA has that delegate defined and that ClassB's delegate property is nil.

Do you intend for Class B to be the delegate of Class A so that it can call back with the -notifyUser method? If that's the case, you don't need the initializer and you should have the delegate property on class B declared as...
#property (assign, nonatomic) id <ExampleDelegate> delegate;
Then, if I'm following you correctly, when you create the detail controller (Class B), you will set it's delegate property to self (Class A). Now when Class B needs to communicate back up to Class A it simply needs to call -notifyUser on it's delegate like so...
// Something happened that you want to communicate back up the chain
[self.delegate notifyUser];
When you use this pattern, the protocol is typically declared on the class that also implements the property for it to be set on, in this case that is Class B.

Edit:
In both classes I have declared delegates as strong variable which needs to be declared as weak as I came through some doc so by changing this it solved the problem.
And thanks for you all the answers which helped a lot.

Related

Changing IBOutlet UIObject in ViewController from another class using Objective C (iOS)

I'm a beginner in iOS development and cannot get this part working. The objective is simple: I have a class named TCPComm which connects to a server and sends data periodically. In my Storyboard I have a view containing some buttons and a textfield. The idea is to change the state of the IBOutlet UIButtons and UITextField based on the received data from the server (from another class).
I have tried using properties in different ways but none of them worked.
Any help on the best way to do this please?
The best way to achieve this is using Delegates.
In your TCPComm header file declare protocol
#protocol tcpCommDelegate <NSObject>
-(void)callBackMethod;
#end
In the interface declare a public property
#property (nonatomic, weak) id<tcpCommDelegate> delegate;
Now, call the method declared in protocol whenever you received some data in your TCPComm class like below
if(delegate && [delegate respondsToSelector:#selector(callBackMethod)])
[delegate performSelector:#selector(callBackMethod)];
Now, in your viewcontroller class make sure you imported TCPComm class and it accepts the tcpCommDelegate protocol like below
#interface YourViewController : UIViewController <tcpCommDelegate>
In viewDidLoad method of ViewController create an instance of TCPComm class and assign its delegate property to self
TCPComm *tcpcomm = [[TCPComm alloc]init];
tcpcomm.delegate = self;
That's, it now you can implement the callBackMethod body in your viewcontroller and change the properties of whatever UIObjects you want. This method will be called whenever there is new data fetched by your TCPComm class, if you have called the 3rd code snippet at the right time i.e when data fetch completed

Setting the delegate from third to first viewController

I have three viewControllers. When I'm on the third view controller I want to send a message to the first one. I'm using a protocol and trying to set the delegate for this.
viewControllerC.h
#protocol ViewControllerCDelegate
- (void)performAction;
#end
...
#property (nonatomic, strong) id<ViewControllerCDelegate> delegate;
viewControllerA.h
#interface ViewControllerA : UIViewController <ViewControllerCDelegate>
viewControllerA.m
...
- (void)performAction {
NSLog(#"Action was performed");
}
So the only problem is, I can't set the delegate from the third to the first. How can I set viewControllerC's delegate to viewControlerA?
Here's an image to describe it:
You'd have to pass along the A controller through to the B controller to be set as the delegate for C when it is created. Kind of messy.
In this case though it might make more sense to use a notification model where Controller A listens for a NSNotification that the action was completed on C.

How do I use delegation in iOS for slide out menu?

I am trying to figure out delegation in iOS. Basically, I have classA which contains methodA. I also have classB which I would like to call methodA from.
To be specific, I have a class called ViewControllerRootHome and class called ViewControllerRootHomeLeftPanel. The ViewControllerRootHome has a method in it called, movePanelToOriginalPosition I would like to call this method from the ViewControllerRootHomeLeftPanel class.
Any help would be greatly appreciated. Ohh forgot to mention I'm still using Objective-C for the project.
I'll give this an attempt.
Let's say you've got a ViewController called ViewControllerA, and another ViewController called ViewControllerB. We want to call a method inside A from B. How are we going to achieve this?
Simple. We will define a protocol inside B that A will comply to. Let me do that right here.
#import ...
#protocol myProtocol; // Declare Protocol
#interface ViewControllerB : UIViewController
#property (nonatomic, weak)id <myProtocol> myDelegate; // Create Delegate property
#end // Notice this is AFTER the #end of the #interface declaration
#protocol myProtocol <NSObject> // Define Protocol
-(void)doSomething;
#end
Okay, now you have defined a protocol called myProtocol that you wish to use inside ViewControllerA
Let us use it there. We will have to do several things: One, comply to the protocol. And two, set our current VC as it's delegate!
#import ...
#import "ViewControllerB" // IMPORT the VC with the Protocol
#interface ViewControllerA : UIViewController <myProtocol> // Conform to Protocl
#property (nonatomic)ViewControllerB *viewControllerB;
#end
Notice I've defined a property of type ViewControllerB. You will need to have a reference to ViewControllerB in some shape or form. This is usually easy to achieve because you normally create an instance of ViewControllerB from ViewControllerA. Otherwise it will need to be set externally or passed to ViewControllerA upon initialization and you set it as a property there.
Inside ViewControllerA.m, set ViewControllerA as it's delegate:
self.ViewControllerB.myDelegate = self;
Now, all you have to do is define the method from the protocol inside ViewController A so it can be called:
-(void)doSomething
{
...
}
This is all you need to do. However, please note that if you have TWO ViewControllers complying to each other's protocols, you will likely have to declare the protocols inside their own header files.
Edit: How to call the method.
If you want to call the method defined inside the protocol. You will do so inside ViewControllerB, like so:
if ([self.myDelegate respondsToSelector:#selector(doSomething)])
{
[self.myDelegate doSomething];
}

Set superclass delegate from subclass setDelegate

I have a superclass A that has a BaseModalViewControllerDelegate protocol and a retain property for the id<BaseModalViewControllerDelegate> delegate.
I also have class B, subclass of A, that has a ModalLoginDelegate protocol and a retain property for the id<ModalLoginDelegate> delegate
Now, I set as setter method for B class delegate this method:
-(void)setDelegate: (id<ModalLoginDelegate>)delegate
{
_delegate = delegate;
[super setDelegate: (id<BaseModalViewControllerDelegate>)delegate;
}
So, there is the RootViewController that implements both protocol, but it inits only B class and it set itself as delegate only for B class because it doesn't know that B class is a subclass of A.
Do you think that this is a correct way to set RootVC as delegate for both protocol? Thanks
ADDING
Setting the protocol of B class as inherited from A class:
#protocol ModalLoginDelegate <BaseModalDelegate>
// delegate method of subclass
#end
Now, my RootVC has not to set itself as delegate of BaseModalViewController. But now, when in my B class I want to call the delegate method of the superclass I'm doing this
if (self.loginDelegate)
{
[self.loginDelegate baseModalViewController: self willDismiss: YES];
}
I think that this is not a very clean way, so I created a public method in the superclass -(void)pressedCloseButton; that it will do this
-(void)pressedCloseButton
{
if (self.delegate)
{
[self.delegate baseModalViewController: self willDismiss: YES];
}
}
And in the subclass:
-(IBAction)closeBtnPressed: (id)sender
{
[super pressedCloseButton];
}
Do you think is right?
A better design is to implement a separate delegate property for your subclass, say loginDelegate. It isn't very good OO design to change the type of a property in a subclass. Most OO languages won't even allow it.
This also ensures that the consuming class is "aware" that there are two separate delegate protocols involved.
RootVC will need to set itself as both delegates if it needs to implement both protocols. You can't expect the class not to know which delegate protocols it needs to implement. If RootVC thinks it is only dealing with the base class then it won't set loginDelegate and won't implement the methods in that protocol.
I don't disagree with Paulw11's answer here at all, but it interesting to note that Apple themselves do this.
example. UIScrollView has a delegate property
#property (weak, nonatomic) id <UIScrollViewDelegate> delegate;
and a subclass, UITableView, has a delegate property
#property (weak, nonatomic) id <UITableViewDelegate> delegate;
When we declare a protocol in ObjC we usually have that protocol extend the < NSObject > protocol.
#protocol BaseModalViewControllerDelegate <NSObject>
-(void)doSomething;
-(NSString *)titleForThing;
#end
Now this protocol above has not only the methods prototyped here but also those in the < NSObject > protocol. Its is very much like this protocol is a 'subclass' of the other protocol, inherits all its stuff too.
If you did this with your second protocol
#protocol ModalLoginDelegate <BaseModalViewControllerDelegate>
-(void)doAnotherThing;
-(NSString *)titleForTheOtherThing;
#end
then what you've done here would be absolutely in line with what Apple have done with UITableView and UIScrollView, because a pointer of type id< ModalLoginDelegate > is always also an object of type id< BaseModalViewControllerDelegate > , just as a UIButton* will always be able to be passed in as a UIView* ...
But without doing this there is a fundamental problem in your
-(void)setDelegate:(id<ModalLoginDelegate>)delegate
method there, because you are assuming this object complies with the BaseModalViewControllerDelegate protocol when the only thing you know for sure is that it complies with the ModalLoginDelegate protocol. Some inherited method may call on self.delegate with a BaseModalViewControllerDelegate method which self.delegate does not respond to..
I hope this helps :)

Receive messages with a protocol?

I'm currently learning Obj-C and more specifically - protocols. I will need to make up a scenario here for my question to make sense.
First example (with a delegate).
I'm a UIView subclass which requests information from it's controller to be displayed (or rather how the information should be displayed). I declare a protocol and make a delegate reference object (or whatever it's called):
#property (nonatomic, weak) id <protocolName> dataSource;
My controller conforms to this protocol and implements the required method.
View sends messages to the Controller, and the Controller answers and everything is fine.
This as far as I know is delegation through protocols and I believe I understand it.
But let's consider another scenario.
I'm a class which is the brain for a simple level-based game. I say when to show the menu or when to start playing a game level. But I need to know when a level is completed.
Which means this class needs to be ready to receive messages from anyone that implements the protocol, for example from another class which is responsible for the current level. Now this is what I don't understand. How the protocol should look like and where/how to implement it?
Let me know if my question still doesn't make sense. Thanks!
Delegate is a one-to-on relationship method for sending message between entities.
If you want to receive the same message from different class, you can use NSNotification that is a one-to-many relationship.
Look at the NSNotificationCenter class, especially postNotification method (for sending messages) and addObserver method (used when received a notification)
EDIT :
Here's an example code for delegation.
Let's take your game level-based example. You have a LevelManager class and FirstLevel class. If you want the LevelManager to be notice when the FirstLevel ended, you have to declare a protocol in your FirstLevel class
// FirstLevel.h
#protocol SomeProtocol <NSObject>
-(void)levelDidEnd;
#end
#interface FirstLevel : NSObject
#property (nonatomic, weak) id<SomeProtocol> delegate;
#end
Then somewhere in your FirstLevel.m, call your protocol method when the level ended
// FirstLevel.m
- (void)playGame {
if ([player isDead]) {
[delegate levelDidEnd];
}
}
The next step is to implement your protocol to your LevelManager :
// LevelManager.h
#interface LevelManager : NSObject <SomeProtocol>
#property (strong, nonatomic) FirstLevel *firstLevel;
#end
And then, set your firstLevel.delegate and implement your protocol method
// LevelManager.h
- (void)viewDidLoad {
firstLevel.delegate = self;
}
- (void) didLevelEnd {
// do actions here like showing the menu
}
You still can go with delegation:
Let the level be a object with an delegate of type id<LevelDelegate>
#protocol LevelDelegate <NSObject>
-(void)completedLevel:(Level *)level;
#end
The BrainController conforms to LevelDelegate and creates all levels. for each level it set itself as delegate.
If a level recognizes it has finished, it calls [self.delegate completedLevel:self];.
The BrainController gets informed and does what ever is necessary.

Resources