This is a fairly complicated inheritance hierarchy, so bear with me (I've tried to simplify things rather than state the exact case I am using which is even more complex):-
Let's say I create a subclass of UITextField called TextField which is my own custom enhanced general-purpose textfield. Now, in order to provide this enhanced functionality, in the init method of TextField, I set super.delegate = self so that all the delegate methods from UITextField are sent to TextField. TextField implements the UITextFieldDelegate protocol and receives those delegate methods to do something interesting.
However, in turn, I want to make it so that TextField has it's own delegate. So I create a new protocol called TextFieldDelegate (note the lack of UI-prefix!) and give TextField an ivar id<TextFieldDelegate> __weak delegate with corresponding property so that other classes can receive delegate methods from TextField.
I hope you're still with me, because I haven't done anything too complex so far. But let's say that now, I create another custom subclass of TextField, let's call it PasswordTextField (in real life, one probably wouldn't need to create a subclass just to implement a password functionality, but let's assume that there is some fairly sophisticated implementation that would require this).
Let's also assume that I want to make it so that PasswordTextField (which like TextField has a delegate property) is able to send an enhanced set of delegate methods. For example, maybe it can send a method passwordIsSecure which is sent once a password has reached a required level of complexity. Now since this behaviour that wouldn't be found in the regular TextField, I create a new protocol: PasswordTextFieldDelegate <TextFieldDelegate> which defines the new delegate methods for PasswordTextField and inherits all of the delegate methods sent by TextField.
The problem is: how do I do implement this in PasswordTextField? Things that don't work:
Inheritance
I cannot simply inherit the delegate from TextField, because TextField's delegate conforms only to TextFieldDelegate and not PasswordTextFieldDelegate, so I can't send methods like [delegate passwordIsSecure] because TextFieldDelegate has no such method.
Overriding ivar
I could try declaring an ivar in PasswordTextField called delegate, but the compiler complains that this is a duplicate declaration, because of course there is already an ivar called delegate in the superclass, so this doesn't work either*.
Modifying the superclass
I could go back to the TextField class and redefine the delegate to implement both TextFieldDelegate and PasswordTextFieldDelegate, but this seems messy and tells TextField that it can send PasswordTextFieldDelegate methods, which of course, it can't!
I haven't tried this one, simply because it seems to break every sensible coding rule in the book.
In summary, there must be some way of doing this such that a subclass of a class can have it's own delegate that's a sub-delegate of the superclass's delegate and for all of this to fit together nicely, but I just can't figure it out! Any ideas?
(* As a side issue, I don't understand why the compiler complains when PasswordTextField declares a "duplicate" ivar named delegate, but doesn't complain when TextField declares an ivar named delegate which is presumably a duplicate of UITextField's property called delegate!)
UITextField delegate ivar is named _delegate, not delegate. Hence why you get away with declaring it again in TextField, but not in PasswordTextField.
As for your delegate inheritance problem. I'm not sure ObjectiveC supports what you want.
You may just have to type your delegate 'id', instead of 'id<TextFieldDelegate>'. Then you could override setDelegate and ensure that the delegate passed in conformsToProtocol. However, you would lose your compile time checks here and only have the runtime check of conformsToProtocol
So, there! works.. and manages to have the compile-time warnings as well..
SimpleParent.h
#protocol Parentprotocol <NSObject>
#end
#interface SimpleParent : NSObject {
id<Parentprotocol> obj;
}
#property (retain) id<Parentprotocol> obj;
#end
SimpleParent.m
#import "SimpleParent.h"
#implementation SimpleParent
#synthesize obj;
#end
SimpleChild.h
#import <Foundation/Foundation.h>
#import "SimpleParent.h"
#protocol SimpleChildProtocol <Parentprotocol>
#end
#interface SimpleChild : NSObject
#property (assign) id<SimpleChildProtocol> obj;
#end
SimpleChild.m
#import "SimpleChild.h"
#implementation SimpleChild
#synthesize obj;
#end
It is a quite confusing question, so forgive me if I'm missing the point, but it seems like your three different inheritance levels each have different requirements from their delegate, ergo each delegate would have to conform to a different protocol, so would it be a solution to hold each level's delegate as a differently named ivar, and as a different reference?
For example, your base class would have its delegate, which you have decided will be assigned to the first inheriting subclass. This has it's own delegate, called level1delegate, and the next level down has another delegate, called level2delegate. You could of course set all three of these to the same object if that object conformed to all three protocols.
Basically, there's no rule that says a delegate has to be called "delegate", so don't tear yourself apart trying not to break it.
Related
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 which 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.
Currently, I have create a separate delegate for each, but I am wondering if there is a better pattern for this? Maybe implement one delegate for ToolbarView, and list the delegate methods from DraggableToolbarView as optional? Or is there a way to subclass a delegate?
What is the best / cleanest way to accomplish this?
If you create a protocol for your delegate methods (always a good idea anyway), you can have another protocol adopt the first. That sets up an inheritance-like relationship:
#protocol ToolbarViewDelegate
// some methods
#end
#protocol DraggableToolbarViewDelegate <ToolBarViewDelegate>
// additional methods
#end
Yes, you can have inheriting protocols:
#protocol Proto1
#reqired
-(void) somethingHappened:(id) sender;
#optional
-(void) somethingElseHappened:(id) sender;
#end
#protocol Proto2<Proto1>
// this now contains all of the method signatures found in proto1, with the addition of new ones!
-(void) somethingSpecialHappened:(id) sender;
#end
I think you're doing it right.
Consider UITextView which is a subclass of UIScrollView. Each has its own delegate protocol that's responsible for reacting to a specific set of messages. As long as you think of visibility and dragging as separate concerns, allowing different objects to handle their delegation seems logical.
I guess this is basic, but I can't get my head around this.
I used to have only one ViewController in which all my variables were defined, e.g. an UITextView named myTextView. I also had methods in this ViewController for handling events that relate to myTextView, such as - ()hideKeyboard { // do something with myTextView or - (void)keyboardWillShow:(NSNotification *)notification { // do something with myTextView.
As my program became bigger and bigger, I thought about using subclasses, especially for other views. So I started a subclass, eg. mySubClass.h and mySubClass.m, in which I had another UITextView (for argument's sake myOtherTextView). In order to incorporate mySubClass, I #imported it into my ViewController and added a #class mySubClass; and could then produce instances of this class so as to use it in my App.
So far so good. As you can imagine, all the nice methods I defined in my ViewController for what should happen when an UITextView is edited (such as hiding keyboard etc.) didn't work for the UITextView in mySubClass.
It was then suggested to me that I should make another class in which I had all the keyboard events and subclass my ViewController and mySubView to it:
#interface ViewController : MyKeyboardEventsViewController
Now, the problem I am seeing is that I won't be able to access all the views, textviews, textfields etc. that I have created in my ViewController (e.g. myTextView which I mentioned earlier).
How can I achieve that all the variables that I have defined in my ViewController will also be available for MyKeyboardEventsViewController? Or is there another way to handle this?
Basically, I don't get how MyKeyboardEventsViewController will be able to access variables in my ViewController which it will need (e.g. the UITextView in question, or the accessoryView which will pop up etc. etc.).
Any suggestions would be very much welcome.
Example:
Class A contains a ivar UITextField textField
Class B subclasses Class A and thus it already contains ivar textField
Note: it's not the other way around. Class A does not "see" what ever is created in Class B.
When ever you subclass a class you give your new class the same ivars end methods of that subclassed class.
I hope this is what you were asking for.
EDIT
So for your example I would do the follwing:
Create a class "MyUIKeybordEventResponder"
Implement all the responder methods like - (BOOL)textFieldShouldReturn:(UITextField *)textField
Subclass your ViewController from "MyUIKeybordEventResponder"
Note method textFieldSHouldReturn has a parameter UITextField so it knows which textfield was pressed. So in a way it receives your textField from the subclass.
If I'm understanding this correctly, you have a UIViewController with MyKeyboardEventsViewController as an instance variable and you want to communicate between the two? If that is the case, one option would be to create a protocol.
#protocol MyKeyboardDelegate
- (void)closeAccessoryView;
#end
(Note - make whatever methods in the protocol that you need, this is simply an example)
In your MyKeyboardEventsViewController you then include the protocol file, and create an ivar
id <MyKeyboardDelegate> delegate;
Also make it a property and synthesize it.
Whatever class that is going to create the keyboardviewcontroller should delcare themselves as conforming to the protocol.
#interface MyViewController : UIViewController <MyKeyboardDelegate>
...
#end
When you create the MyKeyboardEventsViewController, set the delegate.
MyKeyboardEventsViewController *eventsVC = [[MyKeyboardEventsViewController alloc] init];
[eventsVC setDelegate:self];
Now just implement the delegate method and perform whatever action that is necessary.
I need to set my root UIViewController as the delegate for one of its modal child UIViewControllers (runwayAnalysisViewController). I implemented a delegate protocol which my root UIViewController adopts.
[runwayAnalysisViewController setSettingsDelegate: self];
(self being the parent UIViewController)
Unfortunately, I am receiving the classic error, "runwayAnalysisViewController may not respond to "-setSettingsDelegate: method."
the delegate is declared as such in the RunwayAnalysisViewController class:
id <SettingsRequestDelegate> settingsDelegate;
-thence:
#property(nonatomic, assign) id <SettingsRequestDelegate> settingsDelegate;
it is synthesized in the .m file as well.
I have tried synthesizing the accessor & mutator methods for the delegate as well as manually writing the same but to no avail.
(other attempt, methods declared in interface and implemented as shown:)
-(void)setSettingsDelegate:(id)aDelegate {
settingsDelegate = aDelegate;
}
-(id)settingsDelegate {
return settingsDelegate;
}
Strangely enough, while this warning persists, I implemented the single method of this delegate as follows:
#pragma mark - SettingsRequestDelegate Methods
-(void)userDidRequestSettingsAccess:(id)sender {
NSLog(#"User did request settings access");
}
I am able to get a successful message sent from the delegate to the parent UIViewController! Any help would be appreciated.
Are you importing your child controller's *.h file at the beginning of the *.m file of the parent view controller?
(and is the #property line you mention contained in that *.h file?)
You need to declare that your root UIViewController conforms to SettingsRequestDelegate in its header file. So where you currently may have:
#interface RootViewController: UIViewController
You'll instead want:
#interface RootViewController: UIViewController <SettingsRequestDelegate>
It's just a warning because in Objective-C all method call dispatches are resolved dynamically and which methods an object responds to can be changed at runtime (albeit not in a very syntactically pretty way). So even though the compiler thinks at compile time that you've probably made a mistake, it isn't really in a position to be certain.
All you're doing with formal protocols is trying to give the compiler something useful to go on for giving you helpful warnings. They weren't in early versions of Objective-C at all and some informal protocols (ie, the delegate has to have certain methods but you document them only in the API documentation — the compiler is completely out of the loop) survived even into early versions of iOS.
I am trying to call a method in my root view controller from a child view controller such that when I change my options they will automatically update the root view, which will in turn update several other view controllers. For the second part I have used notifications, but for this first I am trying to use a delegate because it (so I have been lead to believe) is a good programming practice. I am having trouble making it work and know that I can set up another notification easily to do the job. Should I continue trying to implement the delegate or just use a notification?
Delegating is a good programming practice for many situations but that doesn't mean you have to use it if you're not comfortable with it. Both delegating and notifications help decouple the view controllers from each other, which is a good thing. Notifications might be a little easier to code and offer the advantage that multiple objects can observe one notification. With delegates, such a thing cannot be done without modifying the delegating object (and is unusual).
Some advantages of delegating:
The connection between delegating object and delegate is made clearer, especially if implementing the delegate is mandatory.
If more than one type of message has to be passed from delegatee to delegate, delegating can make this clearer by specifying one delegate method per message. For notifications, you can use multiple notification names but all notifications end up in the same method on the side of the observer (possibly requiring a nasty switch statement).
Only you can decide what pattern is more appropriate for you. In any case, you should consider not having your view controller send the notification or the delegate message. In many cases, the view controller should change the model and then the model should inform its observers or its delegate that it has been changed.
Implementing a delegate pattern is simple:
In your ChildViewController.h, declare the delegate protocol that the delegate must implement later:
#protocol ChildViewControllerDelegate <NSObject>
#optional
- (void)viewControllerDidChange:(ChildViewController *)controller;
#end
At the top of the file, create an instance variable to hold the pointer to the delegate in your ChildViewController:
#protocol ChildViewControllerDelegate;
#interface ChildViewController : UIViewController {
id <ChildViewControllerDelegate> delegate;
...
}
#property (assign) id <ChildViewControllerDelegate> delegate;
...
#end
In RootViewController.h, make your class conform to the delegate protocol:
#interface RootViewController : UIViewController <ChildViewControllerDelegate> {
...
In the RootViewController implementation, implement the delegate method. Also, when you create the ChildViewController instance, you have to assign the delegate.
#implement RootViewController
...
// in some method:
ChildViewController *controller = [[ChildViewController alloc] initWithNibName:...
controller.delegate = self;
...
- (void)viewControllerDidChange:(ChildViewController *)controller {
NSLog(#"Delegate method was called.");
}
...
In the ChildViewController implementation, call the delegate method at the appropriate time:
#implementation ChildViewController
...
// in some method:
if ([self.delegate respondsToSelector:#selector(viewControllerDidChange:)]) {
[self.delegate viewControllerDidChange:self];
}
...
That's it. (Note: I have written this from memory so there are probably some typos/bugs in it.)
I would like to add:
objects receiving notifications can
react only after the event has
occurred. This is a significant
difference from delegation. The
delegate is given a chance to reject
or modify the operation proposed by
the delegating object. Observing
objects, on the other hand, cannot
directly affect an impending
operation.
Typically, if you need to update the UI based on a change to data in a model, you would have the view controllers observe the relevant model data and update their views when notified of changes.
I see delegation as a bit more formal and like the distinction that Peter Hosey shared recently:
The difference is that delegation is
for to-one (and bidirectional)
communication, whereas notifications
are for to-many, unidirectional
communication.
Also, I have found that (completely) updating the view in viewWillAppear: works fine (but this is not the best solution where performance is a concern).
Notifications can make the runtime behavior of your program significantly more complex. Think of it like a goto with multiple destinations. The order of those destinations is not defined. If you ever crash there is little stack trace information.
There are cases when it makes sense to use notifications--the typical one being to communicate a model change or a global state change to your views. Example, the network is down, the application will resign, etc!
It is worthwhile to learn the delegate pattern in iOS. Delegates give you complete stack traces when you debug. They result in significantly simpler runtime behavior while still achieving the goal of decoupling your objects.
Delegates are a little hard to get used to, but I think it's the best practice and, like Apple, they just work.
I always use the formal protocol declaration. It's a bit more logical in my mind, and it's very clear in the code. I suggest using a UIView to change your options instead of a controller. I always use one main controller and have a lot of subclassed UIViews that the one controller can control. (However, you can modify the following code for a controller, if you really need a controller instead of a normal view.) In the header file of the child view, make it look like this:
// ChildView.h
#import <UIKit/UIKit.h>
#protocol ChildViewDelegate; // tells the compiler that there will be a protocol definition later
#interface ChildViewController : UIView {
id <ChildViewDelegate> delegate;
// more stuff
}
// properties and class/instance methods
#end
#protocol ChildViewDelegate // this is the formal definition
- (void)childView:(ChildView *)c willDismissWithButtonIndex:(NSInteger)i; // change the part after (ChildView *)c to reflect the chosen options
#end
The method between #protocol and the second #end can be called somewhere in the implementation of the ChildView, and then your root view controller can be the delegate that receives the 'notification.'
The .m file should be like this:
// ChildView.m
#import "ChildView.h"
#implementation ChildView
- (id)initWithDelegate:(id<ChildViewDelegate>)del { // make this whatever you want
if (self = [super initWithFrame:CGRect(0, 0, 50, 50)]) { // if frame is a parameter for the init method, you can make that here, your choice
delegate = del; // this defines what class listens to the 'notification'
}
return self;
}
// other methods
// example: a method that will remove the subview
- (void)dismiss {
// tell the delegate (listener) that you're about to dismiss this view
[delegate childView:self willDismissWithButtonIndex:3];
[self removeFromSuperView];
}
#end
Then the root view controller's .h file would include the following code:
// RootViewController.h
#import "ChildView.h"
#interface RootViewController : UIViewController <ChildViewDelegate> {
// stuff
}
// stuff
#end
And the implementation file will implement the method defined in the protocol in ChildView.h, because it will run when the ChildView calls for it to be run. In that method, put the stuff that happens when you'd get the notification.
In this case, you don't need to use either delegation or notification because you don't really need to communicate directly between your views. As gerry3 said, you need to change the data model itself and then let all other views respond to that change.
Your data model should be an independent object that all your view controllers have access to . (The lazy way is to park it as an attribute of the app delegate.) When the user makes a change in View A, View A's controller writes that change to the data model. Then whenever Views B through Z open up, their controllers read the data model and configure the views appropriately.
This way, the neither the views, nor their controllers need to be aware of each other and all changes occur in one central object so they are easily tracked.
Is it really necessary for your root view controller to know about the changes, or just the subviews?
If the root controller does not have to know, having the settings send out the notifications the other views are looking for seems like a better answer to me, as it simplifies code. There is no need to introduce more complexity than you have to.
I'm new to Objective-C and development on Apple's platforms, but hopefully I can frame this question in an understandable way regardless :)
I want to parse an XML feed for my iPhone app, and I decided instead of shoving all of the delegation methods from an instance of NSXMLParser into my view controller, I'd wrap this up inside of a FeedParser class. After reading a few docs and example code, here's what I came up with:
#protocol FeedParserDelegate <NSObject>
- (void)parserDidLoadEpisodes:(NSArray *)episodes;
#end
#interface FeedParser : NSObject {
id <FeedParserDelegate> delegate;
}
#property (nonatomic, assign) id <FeedParserDelegate> delegate;
- (id)initWithURL:(NSURL *)url delegate:(id<FeedParserDelegate>)theDelegate;
#end
Simple. Delegates of my FeedParser object just have to implement parserDidLoadEpisodes.
However, as I started actually making my FeedParser class use NSXMLParser, I realized that I didn't have to specify that FeedParser implements a protocol for NSXMLParser -- I could just implement the delegate methods that I wanted to do something with. I think I've noticed this with other classes that follow the delegate pattern, too, but not all.
So why wouldn't I too just not bother with specifying a formal protocol for my FeedParser class? It would cut away some perhaps unnecessary code. I guess the question is: why would I want to create a formal protocol instead of just doing something like just checking to see if the method is implemented on the delegate with respondsToSelector? Is it only so that the compiler will issue nice warnings if a required delegate method isn't implemented?
A protocol declared with #protocol is called a "formal protocol". The other way to do it is to declare a category (a set of additional methods) on NSObject, like so:
#interface NSObject (FeedParserDelegate)
- (void)parserDidLoadEpisodes:(NSArray *)episodes;
#end
Then simply define that method for any object that you want to be a feed parser delegate, and leave it undefined otherwise. This is called an "informal protocol".
Why are there two ways? Well, here's a hint: the informal protocols came first. What it comes down to is that they added formal protocols because informal ones weren't cutting it. Informal protocols make it too easy to forget an important method or try to use an object as a delegate for something it's not designed to work with.
Basically, for the cost of adding <FeedParserDelegate> here and there, you can get the compiler to do your debugging for you. The compiler will generate warnings for the most common delegate bugs, which saves you time if you make one of those mistakes. Why not take advantage of its helpfulness?
Always, always, always favor compile-time error checking over run-time. You could of course ask the class if it supports the method, but why ask when you can know?
The answer is no, you don't have to. But you should want to. :)
Adding a protocol makes the compiler check things for you. If you don't get those nice errors at compile time, then you'll get them later at run time when they're harder to track down.