If I want to reuse a protocol to notify the master view controller of when the detail view controller is dismissed such as this:
UIView notification when modal UIImagePickerController is dismissed?
where would i declare this protocol? Is it best practices to keep protocols in separate files? Thanks.
Typically I (copying Apple's methods) declare protocols on the header of the class that will be interacting with delegates of that protocol. (E.g. the UIActionSheet header file holds the protocol declaration for UIActionSheetDelegate.) It doesn't really matter technically where you declare protocols, as long as you don't have a circular reference of #import statements. This won't normally happen because the class that interacts with the protocol does so to avoid needing to #import all of the other classes that will now implement the protocol. (UIActionSheet sends messages to your classes when it is dismissed through the protocol, and therefore doesn't need to #import any of your classes.)
If you have a protocol and several unrelated classes will be interacting with delegates of that protocol, that would be a good indicator to put the protocol in its own file, because the protocol isn't really associated with one particular class.
I think you should declare a catagory of UIImagePickerController in separate files.Like ImagePickerCtrlCatagory.h and ImagePickerCtrlCatagory.m
In the ImagePickerCtrlCatagory.h ,do like this
#import <Foundation/Foundation.h>
#protocol ImagePickerCtrlDelegate;
#interface UIImagePickerController (DissappearDelegate){
id <ImagePickerCtrlDelegate> delegate;
}
#end
Related
I am relatively new to Objective-C.
I have come to a code on the web that has something like this on rootViewController.m (this is a navigationController based app).
#interface RootViewController (CManagerDelegate) <CManagerDelegate>
#end
#interface RootViewController (PViewDelegate) <PViewDelegate>
#end
two questions:
what are these lines doing in the beginning of rootViewController.m
what are these lines doing in code? Please explain the stuff in parenthesis and between <> in this particular case.
thanks.
In one sentence: The code you posted makes the RootViewController class privately conform to some delegate protocols.
Delegate protocols are used to let a class declare the fact that it understands the messages from another class's objects. For example, a view controller can declare that it understands a gesture recognizer's delegate messages.
The fact that the class internally uses the gesture recognizer is often an implementation detail not relevant to other clients of the class. It is best not to publish this fact in the public interface but put it into the implementation (.m file).
Categories (and class extensions) let you do exactly this: Make a class conform to a protocol without changing the main #interface.
A nice and elegant solution!
Read up on Categories:
http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/chapters/occategories.html
And Protocols:
http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/objectivec/chapters/ocProtocols.html
In fact, read all of Apple's Objective-c documentation before going any further:
http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/objectivec/Introduction/introObjectiveC.html
Good luck.
I'm getting the following error
Property 'navigationController' not found on object of type
I've inherited this project and not sure what's going on.
In my m file in didSelectRowAtIndexPath I have
[self.navigationController pushViewController:nextController animated:YES];
It wasn't a problem before as I was accessing app delegate navigation controllers, which were outlets. However, I've had to move away from this approach as it's causing me problems. I've converted the rest of my project. But in this circumstance, where the project isn't using a normal table view, the navigation controller doesn't look to be available. I have this issue in 7 other views. Hoping I could have fixed this, and keep this cleaner code?
I'm really puzzled by this, I think this is occuring as SetsStyleSectionController isn't a view controller but is NSObject.
However, even with this set to UIViewController, the code runs, but doesn't push either.
Changing SetsSectionController from NSObject to UIViewController isn't available.
I'm not sure how to proceed?
I'm in the process of moving away from pushing from app delegate.
Edit: Screenshot 2 discussed below
I see a couple of issues here. You have a misunderstanding about protocols and classes, and you also have an application that interface with a protocol that while well-intentioned is actually making your life much harder than it needs to be.
The first issue you're dealing with is some troubles grokking the difference between protocols and classes, and between adopting a protocol and inheriting from a class. Which is totally fine, this stuff isn't easy. Basically, a protocol is just an interface to an object, and a class provides both an interface and an implementation. In other words, a protocol is just a list of methods that can be called, and a class is both a list of methods and the code to execute those methods. To get a more complete explanation, perhaps you'll be better off going straight to the source - Apple's "The Objective-C Programming Language" will probably help you, so please read about classes and protocols there. I think having done that you'll see why you're not having success giving your id<SetSectionController> instance a navigationController property without explicitly defining one. But do let me know if you have specific questions about this afterwards.
The problem that's harder to fix is this SetSectionController protocol. It has several issues and describing them all is outside the scope of this answer. Here's the thing - the implementation basically requires objects that implement this protocol to know which navigation controller is associated with the table view. This has been up to now provided deus ex machina by coupling them to the application's delegate, and you are right to remove this coupling. But now you have to find another way to get the right data populated into the view controller to push it on the navigation stack.
I think you should move the push logic into the view controller, and for now have the section controller provide an interface that gives the view controller the information it needs. So say the section controller has an implementation like this pseudocode:
- (void)...didSelectRow...
{
id detailsForIndexPath = self.dataForRows[indexPath.row];
DetailViewController *vc = [DetailViewController new];
vc.details = detailsForIndexPath;
[APPDELEGATE.navigationController push:vc];
}
Then I'd add a method to SetSectionController called something like -dataForRow: , the implementation of which would be like the first line of the method above. Then, in your view controller, implement ...didSelectRow... like this:
- (void)...didSelectRow...
{
id<SetSectionController> sc = self.sectionControllers[indexPath.section];
id details = [sc dataForRow:indexPath.row];
DetailViewController *vc = [DetailViewController new];
vc.details = details;
[self.navigationController push:vc];
}
If your section controller is doing anything else useful in ...didSelectRow... make sure to either move it to the view controller or forward ...didSelectRow... on to the section controller for now.
While I do appreciate trying to make complex table sections easier to manage through polymorphism, this protocol wasn't the right way to do it. It blindly copies methods out of UITableViewDelegate and UITableViewDataSource without consideration of whether those are the right questions to be asking something responsible for a single section of a single table. If you still want to use it, I think it will take some significant refactoring to get it into a shape that actually makes your life easier rather than harder. Depending on the complexity of the per-section logic deviation, I might just scrap it altogether. But that's a whole other question really. Hope this helps!
What do you mean it "isn't available"? Do you mean you don't want to/aren't allowed to subclass UIViewController, or are you getting a compiler error? From your comment on your question:
SetsSectionController.h:12:34: Cannot find protocol declaration for 'UIViewController'
you are changing the wrong thing to alter your subclass. As an example:
#import <Foundation/Foundation.h>
#protocol foo <NSObject>
- (void) bar;
#end
#interface lolcats : NSObject <foo>
#end
To change your superclass you change the class name after the colon. Ex.
#interface lolcats : NSObject <foo>
becomes
#interface lolcats : UIViewController <foo>
and you're off and running. However, if you accidentally change the protocol requirement for the protocol
#protocol foo <NSObject>
to
#protocol foo <UIViewController>
instead, or change the protocol you adhere to to <UIViewController>, you'll end up getting the EXACT error you got. You might be confused because the protocol says the object adhering to it must also adhere to the NSObject protocol, and NSObject is also a type of object. The object and protocol are separate things, and all objects inherit from NSObject, and thus inherit the protocol. Basically, it's saying "objects using this protocol must be objects."
Why did you expect this to work? The object is just a standard NSObject that states it adheres to a protocol containing a few methods. Why would it have a navigation controller property? It hasn't inherited it from anything. Regardless, based on your error, you probably just changed the wrong thing. Make sure you change the superclass class name after the colon.
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.
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.
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.