How can I invoke a method in a class only after verifying a condition in another method of another class in my iPhone app?
Any ideas?
Thanks, Andrea
edit 3
//class1
//Class1.m
#implementation Class1 {
....
[class2 method1:#"file1.xml"];
[class2 method1:#"file2.xml"];
[class2 method1:#"file3.xml"];
}
….
#end
//class2
#import "Class1.h"
#implementation Class2{
-(void) method1(NSString *)file{
[self method2];
}
-(void) method2{
//when finish that method I have to call the successive method [class2 method1:#"file2.xml"]; in class1
}
}
hope this help to understand (even better) the problem...
You need to use delegation. Making class 1 class 2's delegate lets class 2 send messages to class 1.
Edit changes: You want class2 to be the delegate of class 1. This means that class 1 will tell class 2 to perform method1 with whatever comes after after the colon. This can be any object. In the example, I used a string. Process method1 as you normally do, but remember that the xmlFile variable should be used instead of a hardcoded object, i.e. use xmlFile instead of #"file1.xml".
EDITED Example:
class 1 .h:
#import <UIKit/UIKit.h>
..etc
//a protocol declaration must go before #interface
#protocol class1Delegate
-(void)method1:(NSString *)xmlFile;
#end
#interface class1 {
id <class1Delegate> delegate;
}
#property (nonatomic, assign) id <class1Delegate> delegate;
#end
Synthesize delegate in your .m
Then call [delegate method1:#"file1"].
class 2 .h:
#import "class1.h"
#interface class2 <class1Delegate> {
//put whatever here
}
- (void)method1:(NSString *)xmlFile;
Related
I want to invoke a delegate in class method.
The example below obviously does not work, since the delegate is an instance variable that is accessed within a class method. (Error: instance variable 'delegate' accessed in class method)
Is there an alertnative?
My header file:
// MyClass.h
#import <Foundation/Foundation.h>
#protocol MyDelegate <NSObject>
-(void)update;
#end
#interface MyClass : NSObject
{
id<MyDelegate> delegate;
}
#property (nonatomic, retain) id delegate;
+(void)methodThatInvokesDelegate;
#end
My implementation file:
// MyClass.m
#import "MyClass.h"
#implementation MyClass
#synthesize delegate;
+(void)methodThatInvokesDelegate{
[delegate update];
}
#end
Three obvious options:
Singleton
Static variable (i.e., class variable) pointing to the delegate
Use NSNotification's rather than delegates
Since a singleton (and a static variable) can't keep track of the lifecycle of delegates, I think option three would be the cleanest.
I want to know the context, which let you run in that situation. ;-) Anyway:
First: Delegates are set for a specific instance object. Because of this, you can have different delegates for different instances of the same (delegating) class.
Second: A class method runs inside a class object of that class. This is an object that is different from every instance object of that class. So there is nothing that can be called "the delegate". You can have 100s of delegates.
Third: Your class object needs a delegate at its own. So you have to add a property delegate to the class object and then use this. (Yes, it is possible to have properties an a class object. I did not write declared property.) If you need further information on how to do this, just comment it. I will add code.
I'm not sure if this will help you, but I have a similar situation where I have a class method used for data loads. In this case, the class instantiates itself (so that the caller doesn't need to) until it is done. (this code was edited somewhat to make it work here)
header file:
#protocol DataLoaderDelegate2 <NSObject>
- (void) dataLoaderSuccess:(NSData *)data loader:(id)theloader;
- (void) dataLoaderFailed:(NSString *)error loader:(id)theloader;
#end
#interface DataLoader2 : NSObject {
NSURLConnection *conn;
NSMutableData *receivedData;
NSFileHandle *fileHandle;
id <DataLoaderDelegate2> delegate;
}
#property (nonatomic, assign) id<DataLoaderDelegate2>delegate;
Call to start the process - the call to initWithRequest passes "self" along.
+ (DataLoader2 *)loadWithURLRequest:(NSURLRequest *)req delegate:(id)_delegate
{
DataLoader2 *dl = [[DataLoader2 alloc] init];
[dl setDelegate:_delegate];
conn = [[NSURLConnection alloc] initWithRequest:req delegate:self];
return dl;
}
When the data is done loading, it cleans up with something like
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if ([delegate respondsToSelector:#selector(dataLoaderSuccess:loader:)])
[delegate dataLoaderSuccess:(fileHandle)?(id)fileHandle:(id)receivedData loader:self];
[self autorelease];
}
I have a class called ToolbarView which is a subclass of UIView and basically creates a UIView that has a disappearing / reappearing UIToolbar on top. I also have a subclass of ToolbarView called DraggableToolbarView enables the user to drag the view around the screen.
I need to create a delegate for ToolbarView so it can notify another object / class of when the toolbar reappears and disappears. I also need to create a delegate for DraggableToolbarView so I can notify another object / class when the view is dragged. DraggableToolbarViews delegate will also need to notify another object / class of when the toolbar reappears and disappears.
So I decided to implement ToolbarViewDelegate, and have DraggableToolbarViewDelegate inherit from it and have its own method like following:
ToolbarView.h
#import <UIKit/UIKit.h>
#protocol ToolbarViewDelegate;
#interface ToolbarView : UIView <UIGestureRecognizerDelegate>
{
id <ToolbarViewDelegate> _toolbarViewDelegate;
}
#property(nonatomic, assign) id <ToolbarViewDelegate> toolbarViewDelegate;
#end
ToolbarView.m
#import "ToolbarView.h"
#import "ToolbarViewDelegate.h"
...
- (void) showBars
{
...
if (self.toolbarViewDelegate)
{
[self.toolbarViewDelegate toolbarViewWillShowToolbar:self];
}
...
}
- (void) hideBars
{
...
if (self.toolbarViewDelegate)
{
[self.toolbarViewDelegate toolbarViewWillHideToolbar:self];
}
...
}
ToolbarViewDelegate.h
#class ToolbarView;
#protocol ToolbarViewDelegate
#required
- (void) toolBarViewWillShowToolbar:(ToolbarView *)toolbarView;
- (void) toolBarViewWillHideToolbar:(ToolbarView *)toolbarView;
#end
DraggableToolbarView.h
#import "ToolbarView.h"
#protocol DraggableToolbarViewDelegate;
#interface DraggableToolbarView : ToolbarView
{
id <DraggableToolbarViewDelegate> _draggableToolbarViewDelegate;
}
#property(nonatomic, assign) id <DraggableToolbarViewDelegate> draggableToolbarViewDelegate;
#end
DraggableToolbarView.m
#import "DraggableToolbarView.h"
#import "DraggableToolbarViewDelegate.h"
...
- (void)drag:(UIPanGestureRecognizer *)sender
{
...
if (self.draggableToolbarViewDelegate)
{
[self.draggableToolbarViewDelegate draggableToolbarViewWillDrag:self];
}
...
}
...
DraggableToolbarViewDelegate.h
#import "ToolbarViewDelegate.h"
#class DraggableToolbarView;
#protocol DraggableToolbarViewDelegate <ToolbarViewDelegate>
#required
- (void) draggableToolbarViewWillDrag:(DraggableToolbarView *)draggableToolbarView;
#end
SomeViewController.h
#import <UIKit/UIKit.h>
#import "ToolbarViewDelegate.h"
#import "DraggableToolbarViewDelegate.h"
#interface SomeViewController : UIViewController <ToolbarViewDelegate, DraggableToolbarViewDelegate>
{
}
#end
SomeViewController.m
#import "DraggableToolbarView.h"
...
- (void) toolbarViewWillShowToolbar:(ToolbarView*)toolbarView
{
//NSLog(#"Toolbar Showed");
}
- (void) toolbarViewWillHideToolbar:(ToolbarView*)toolbarView
{
//NSLog(#"Toolbar Hidden");
}
- (void) draggableToolbarViewWillDrag:(DraggableToolbarView*)draggableToolbarView
{
//NSLog(#"Dragged");
}
...
[draggableToolbarView setDraggableToolbarViewDelegate:self];
...
When I do this only the DraggableToolbarDelegate methods are responding. However when I also do [drabbleToolbarView setToolbarViewDelegate:self] it works. I've tried doing each delegate separately without inheritence and it works fine so I believe the problem isn't in any other part of the code.
Anyone might know why? I figured by making the protocols inherit, I wouldn't also have to set the ToolbarViewDelegate for a DraggableToolbar object.
UPDATE: Added a lot more code
In your code, any given DraggableToolbarView instance has two properties to connect to delegates, one called toolbarViewDelegate which it inherits from its superclass, and one called draggableToolbarViewDelegate which is defined in DraggableToolbarView itself. You've got to set both of those if you want the controller to get all the delegate messages.
What you're trying to do is possible, however. You need to use the same property name in both your view classes, so that there is only one delegate connection for any instance.
First, change the name of the delegate in the superclass. (Note that you don't need, and indeed shouldn't bother, to declare an ivar for the property -- it's created by #synthesize.)
#interface ToolbarView : UIView <UIGestureRecognizerDelegate>
#property (nonatomic, assign) id <ToolbarViewDelegate> delegate;
#end
You will use the same property name in the subclass.
#interface DraggableToolbarView : ToolbarView
#property (nonatomic, assign) id <DraggableToolbarViewDelegate> delegate;
#end
This is allowed as long as the name of the backing ivar in the subclass is different than that of the superclass, e.g.,
// In superclass
#synthesize delegate;
// In subclass
#synthesize delegate = delegate_;
Now change all the delegate messages in the two view classes to use this one property:
- (void)showBars
{
if (self.delegate)
{
[self.delegate ...
- (void)drag:(UIPanGestureRecognizer *)sender
{
//...
if (self.delegate)
{
[self.delegate ...
Now you can send setDelegate: to a DraggableToolbarView and it will use the same delegate for the dragging methods and the show/hide methods.
Finally, a terminology/explanatory note. In response to your previous question, Caleb used the correct term for "stacked" protocols, and Richard did not. Protocols don't inherit from each other, but one protocol can adopt the other. The relationship is similar, but distinct. When an object conforms to a protocol, it promises to implement the methods declared in that protocol. No implementation comes along with the protocol. The same is true of one protocol adopting the other -- the methods are just declared to exist in both. When you write:
#protocol DraggableToolbarViewDelegate <ToolbarViewDelegate>
you are saying that any object which promises to implement DraggableToolbarViewDelegate's methods will also implement the methods from ToolbarViewDelegate. That's all that it means. Again, no implementation comes along with that promise.
In this case, that means that a DraggableToolbarView can expect its delegate to implement the methods in ToolbarViewDelegate.
You have not given the entire code, but from whatever is out here,
Make sure that
Your ToolBarView and its subclasses have an id <ToolBarViewDelegate> delegate as a property.
Your DraggableToolbarViewDelegate extends NSObject protocol.
and your other ViewController object conforms to delegate protocol and not the toolbarview.
Once your controller gives implementation of delegates methods and conforms to the protocol, set the delegate of view's object to self and then use delegate property set in the view to call these protocol methods.
Here's my scenario. I have a class A. Inside its implementation I create object of type B and set B's delegate to self (So B.delegate = self somewhere inside class A's implementation).
And class A has an instance method - (void)printThis;
Now inside B's implementation, when I try to do [delegate printThis];, it gives me this error:
"No known instance method for selector printThis"
Of course this is when I have enabled ARC. The above delegation pattern used to work fine in iOS 4.x without the ARC. And it still does when I switch OFF ARC. What has ARC got to do with passing messages to delegates?
Skeleton code:
A.h
#class B;
#interface A: blah blah
{
B objB;
}
-(void) printThis;
A.m
objB = [[B alloc] init];
objB.delegate = self;
- (void)printThis {
//doSomething
}
B.h
#interface B: blah blah
{
//id delegate; //used to be there, now I just property & synthesize
}
#property (nonatomic,weak) id delegate;
B.m
#synthesize delegate;
[delegate printThis]; //error with ARC ON, works with OFF
IMPORTANT EDIT:
And mind you this happens for a method here and there. For instance I have a few other methods in A like printThat etc etc which work without errors. I'm clueless as to what is happening!
You need to define -printThis in a protocol and make A implement this protocol. You also need to mark the delegate as conforming to this delegate.
i.e.:
#protocol Printer <NSObject>
- (void)printThis;
#end
#interface A : NSObject <Printer>
//...
#end
#interface B : //...
#property (nonatomic, weak) id<Printer> delegate;
#end
ARC needs to know about the interface for method calls in order to properly manage the memory correctly. If there isn't a definition then it'll complain.
I try to learn protocol of objective C.
I write two files, the first one is FirstViewController.h, and in which there is a protocol "print". I declare FirstViewController class in successViewController with the delegate method "print".
The question is why the console output is "C". Why I can not get the "B" output? Why the protocol method did not perform?
#import <UIKit/UIKit.h>
#import "FirstViewController.h"
#interface successViewController : UIViewController <FirstViewControllerDelegate> {
}
#end
#import "successViewController.h"
#import "FirstViewController.h"
#implementation successViewController
- (void)viewDidLoad {
FirstViewController *firstViewController= [[FirstViewController alloc] init];
firstViewController.delegate=self;
NSLog(#"C");
[super viewDidLoad];
}
-(void) print{
NSLog(#"B");
}
#end
#import <Foundation/Foundation.h>
#class FirstViewController;
#protocol FirstViewControllerDelegate <NSObject>
- (void) print;
#end
#interface FirstViewController : NSObject {
id <FirstViewControllerDelegate> delegate;
}
#property (nonatomic, assign) id <FirstViewControllerDelegate> delegate;
#end
#import "FirstViewController.h"
#implementation FirstViewController
#synthesize delegate;
#end
Because you never call the print method. Where were you expecting it to be called?
Objective-C protocols allow you to specify that a class is capable of performing certain actions. In your example, the successViewController is declared FirstViewControllerDelegate, meaning it is capable of handing the duties required by FirstViewController of its delegate. It is more of a programming contract between classes, one that can be verified by the compiler.
As a side note, classes in Objective-C should always start with a capital letter, methods should always start lowercase. Your FirstViewController follows this rule, but the successViewController does not.
You need to call the method you want to use.
[successViewController print];
You never call the delegates print method. A delegate can not read your mind and automagically call stuff. Lets take a small example how how delegates are supposed to work.
Assume we have a class called Delay, the only thing it do is to wait for a time when start is called, and then tell it's delegate that it has waited. Optionally the delegate can tell the Delay how long to wait, if the client do not care, a 1 second delay is assumed.
Some rules:
First argument of all delegate methods should be the sender itself, never have delegate methods with no arguments.
Delegate method name should include one of the words:
will - if method is called before something unavoidable occurs. Example applicationWillTerminate:
did - if method is called after something has occurred. Example scrollViewDidScroll:
should - if the method return a BOOL to signal if something should occur. Example textFieldShouldClear:
Name the method to tell what has occurred, not what you expect the delegate to do.
Only exception is if the client is expected to return something, then that something should be part of the name. Example: tableView:editingStyleForRowAtIndexPath:
Here is the simple definition and implementation. Notice that I do not even check if the delegate has been set, since calling methods on nil is just ignored anyway.
// Delay.h
#protocol DelayDelegate;
#interface Delay : NSObject {
#private
id<DelayDelegate> _delegate;
}
#property(nonatomic, assign) id<DelayDelegate> delegate;
-(void)start;
#end
#protocol DelayDelegate <NSObject>
#required
-(void)delayDidComplete:(Delay*)delay;
#optional
-(NSTimeInterval)timeIntervalForDelay:(Delay*)delay;
#end
// Delay.m
#interface Delay
#synthesize = delegate = _delegate;
-(void)start {
NSTimeInterval delay = 1.0;
if ([self.delegate respondsToSelector:#selector(timeIntervalForDelay:)]) {
delay = [self.delegate timeIntervalForDelay:self];
}
[self performSelector:#selector(fireDelay) withObject:nil afterDelay:delay];
}
-(void)fireDelay {
[self.delegate delayDidComplete:self];
}
#end
I want to subclass UITextView, and send a new message to the delegate. So, I want to extend the delegate protocol. What's the correct way to do this?
I started out with this:
interface:
#import <Foundation/Foundation.h>
#class MySubClass;
#protocol MySubClassDelegate <UITextViewDelegate>
- (void) MySubClassMessage: (MySubClass *) subclass;
#end
#interface MySubClass : UITextView {
}
#end
implementation:
#import "MySubClass.h"
#implementation MySubClass
- (void) SomeMethod; {
if ([self.delegate respondsToSelector: #selector (MySubClassMessage:)]) {
[self.delegate MySubClassMessage: self];
}
}
#end
however with that I get the warning: '-MySubClassMessage:' not found in protocol(s).
I had one way working where I created my own ivar to store the delegate, then also stored the delegate using [super setDelegate] but that seemed wrong. perhaps it's not.
I know I can just pass id's around and get by, but My goal is to make sure that the compiler checks that any delegate supplied to MySubClass conforms to MySubClassDelegate protocol.
To further clairfy:
#interface MySubClassTester : NSObject {
}
#implementation MySubClassTester
- (void) one {
MySubClass *subclass = [[MySubClass alloc] init];
subclass.delegate = self;
}
#end
will produce the warning: class 'MySubClassTester' does not implement the 'UITextViewDelegate' protocol
I want it to produce the warning about not implementing 'MySubClassDelegate' protocol instead.
The UITextView defines its delegate as
#property(nonatomic, assign) id<UITextViewDelegate> delegate
meaning it conforms to UITextViewDelegate, and that's what compiler checks. If you want to use the new protocol, you need to redefine delegate to conform to your protocol:
#interface MySubClass : UITextView {
}
#property(nonatomic, assign) id<MySubClassDelegate> delegate
#end
The compiler shouldn't give any more warnings.
[Update by fess]
... With this the compiler will warn that the accessors need to be implemented... [I implemented this:]
-(void) setDelegate:(id<MySubClassDelegate>) delegate {
[super setDelegate: delegate];
}
- (id) delegate {
return [super delegate];
}
"
[My update]
I believe it should work if you only make a #dynamic declaration instead of reimplementing the method, as the implementation is already there:
#dynamic delegate;
For anyone still interested, this can be done quite simply like this (for sake of the example, I subclass UIScrollView):
#protocol MySubclassProtocol <UIScrollViewDelegate>
#required
-(void)myProtocolMethod;
#end
#interface MySubClass : UIScrollView
#property (nonatomic, weak) id <MySubclassProtocol> delegate;
The most important detail here is the part between the <> after your protocol's name which, put in a simple manner, signals you're extending that protocol.
In your implementation, all you need to do then is:
#synthesize delegate;
And you're done.
You need to extend the super protocol:
#protocol MYClassProtocol <SuperClassProtocol>
-(void)foo;
#end
after that DON'T (!!!) create the #property for the delegate otherwise you override the original delegate object, but simply override the method:
- (id<MYClassProtocol>)delegate
{
return (id<MYClassProtocol>)[super delegate];
}
now you can use the delegate in the classic way:
[self.delegate foo];
[self.delegate someSuperClassDelegateMethod];
Given that MySubClassMessage: is optional, you should be able to simple do a simple:
- (void) SomeMethod {
SEL delegateSelector = #selector(MySubClassMessage:);
if ([self.delegate respondsToSelector:delegateSelector]) {
[self.delegate performSelector:delegateSelector withObject:self];
}
}
The complier should still check that the implementing class conforms to your protocol (or at least claim to in the header) and you won't get the error you described.