When/why/how would you use these methods?
- navigationController:willShowViewController:animated:
– navigationController:didShowViewController:animated:
Can't you just use these UIViewController Instance Methods instead?
– viewWillAppear:
– viewDidAppear:
– viewWillDisappear:
– viewDidDisappear:
You'd use the first ones if you want to be informed about these events outside the visible view controllers. The delegates allow you to get a notification at a single point. Using UIViewController's methods bind you within these controllers, where you'd have to write/call same code multiple times to achieve the same.
Generally you'd divide tasks into these two groups:
Things that happen across all view controllers: use the delegates
Things happening within a single view controller: use the instance methods
The UINavigationControllerDelegate protocol defines methods a navigation controller delegate can implement to change the behavior when view controllers are pushed and popped from the stack of a navigation controller.
These method are important when you need to perform some actions that would not be on the scope of your view controller. the delegate it's supposed to be a object predecessor of your view controller on the hierarchy and that would be interested in perform some actions without the knowing of each view controller that is pushed or popped, these actions are not necessarily related with that view controller specifically, but they can be methods called on other objects.
Related
In order to respect the MVC pattern, I would like to dissociate the view from the controller.
For example
QuestionView (inherits from UIVIew)
QuestionViewController (inherits from UIViewController
In my controller, I set the view self.view = ...
But when I push a button in the view, it should call a method from the controller BUT the view shouldn't know its controller right ?
So how can I link the view to the controller ?
Set button target as QuestionViewController's object programmatically.
With iOS in most cases you can get the job done with only sub classing the controller part, and not the views. So you use the UIKit provided classes straight 'out of the box'.
It's is possible because:
The layout: this can be stored in a Nib file and be loaded by the controller.
responding to user events: the UIcontrols have generic callback mechanisms: delegates and actions. The 'connection' is either made in the nib file, or in the controller code.
Personally, I only subclass views when I need custom drawing.
So, the View INSTANCE obviously 'knows' its controller, but it is all done through generic interfaces, and so the view CODE is ignorant of your controller.
I'm not going to say this is good design by any means. I'm kind of inheriting something that is existing. Anyway, there is a TabController. One this one tab, there are two views that get loaded on demand based on a UISegmentController. Both of these two ViewControllers are subclasses of another ViewController that has methods I need.
When I'm in the TabController, I want to create a method that uses some of the superclass methods of the two ViewControllers. How do I get access to the tab's current ViewController since it's loaded on demand? Do I need to have a reference of the base controller type, and just have it set to the current view controller when it gets loaded on demand? Thanks.
To get the active tab showing on the screen, you could use [self.tabBarController selectedViewController] which will give you a UIViewController Reference. If you want to use the methods then you can cast it to your ViewController Superclass and then call methods on it like so (where self.tabBarController is your Tab Bar Controller):
MySuperClassViewController *viewC = (MySuperClassViewController *)[self.tabBarController selectedViewController];
[viewC someMethodDeclaredHere];
First, a little bit of terminology to clear up some potential confusion: I assume you mean a UITabBarController which controls various view controllers. Also, it seems you are using a UISegmentedControl which is not a view controller but a subclass of UIView. I hope these are just typos and not conceptional problems.
There is still some ambiguity about what you mean with "when I am in the tab controller". I assume you want to put code into the class representing the UITabBarController. But why? Just put these methods into the appropriate view controller, or if it is something that has to be done before, into your app delegate. However, if these methods are in a view controller superclass, they should have something to do with the logic necessary for this view controller.
If you need the methods elsewhere, i.e. outside your view controllers, consider creating a separate #include file and putting the methods there. Alternatively, you can use your app delegate which is conveniently accessible through [[UIApplication sharedApplication] delegate].
From within a UIViewController, I programmatically create about 10 instances of a CustomUIView and place them on the [UIViewController view].
When any of those instances of CustomUIView is touched, I want to let the controller know and take some action.
I've thought of two ways to do this:
Subscribe the UIViewController as an observer to the CustomUIView. Then when touchesBegan:withEvent: is fired, call a method on the UIViewController observer.
Use [self.nextResponder touchesBegan:touches withEvent:event] to raise the event to the UIViewController.
I've implemented both, and they both work ok.
Question: What is the best way for a programmatically-created view to communicate with its UIViewController? Is one of these the way to go? Or is there a third way?
The resources I've found online are very good at explaining all the Cocoa "pieces", but I'm having trouble finding best practices for overall architecture.
For the case of touch events, you don't have to do anything!
From the UIViewController class reference:
View controllers are tightly bound to the views they manage and take part in the responder chain used to handle events. View controllers are themselves descendants of the UIResponder class and are inserted into the responder chain between the managed root view and its superview, which typically belongs to a different view controller. If the view controller’s view does not handle an event, the view controller itself has the option of handling the event before passing the event along to the superview.
This means your view controller can handle touch events in its view without any extra setup, as long as you don't handle them in your view.
You would make the UIViewController the delegate for the UIViews, if you want the views to be able to communicate back to the controller.
Touch events will automatically get sent to the Controller as part of the responder chain.
I have the following structure in my app:
Custom View Controller
+- Custom View 1
+- Custom View 2
+- A number of UIControls
If the user taps one of the UIControls I would like to send a message to my custom view controller.
Currently I can see two solutions for this:
Tell the 1st custom view about the controller, then tell the 2nd custom view about it as well, and set the target and action when I create the 'UIControl's. (My custom views could have a -initWithFrame:controller: method or something)
The UIControl could send an NSNotification (possibly with some userInfo) that my controller observes.
I'm leaning toward option 2 because I dislike telling Custom View 1 about my controller, just so it can tell Custom View 2 about it.
What are the pros and cons for my two solutions, or is there another way to do this?
Update: I went with the NSNotification for now.
How about you keep a pointer to your Custom View Controller from your app delegate and expose it as a property.
Then you can use the static sharedApplication message on UIApplication to get to your app delegate and the corresponding property:
// in custom view 2 code ...
YourApplication * app = (YourApplication*)[UIApplication sharedApplication];
CustomViewController * cvc = app.customViewController;
There are several possibilities but the best one really depends on your particular business case, and the purposes of several nested UIView subclasses.
Assign a value to the tag property of the control, then access it using viewWithTag: from the view controller when you are setting up it's target-action events.
Provide public accessors on each UIView subclass for each subview, then access the control from the view controller using these properties.
Use the responder chain. Specify a nil target when setting up the control's target-action and the action will eventually be sent to the view controller if it implements it.
Depending on the nature of your UIView subclasses, it might be better to implement any necessary callbacks to the view controller through a delegate protocol of the view. So the target-action of the control would be assigned by the containing UIView subclass to itself, and in it's handler it would call it's own delegate method.
I have a UIViewController — let's call it "FormController" — which is simply a form that edits an object. I want to use it in 2 different situations:
Creating a new object — using using UINavigationController's presentModalViewController: method.
Editing an existing object — push the view controller onto the UINavigationController stack, not using a dialog method.
There is a slight difference in that in the modal situation I would like to have a toolbar with "Cancel" and "Done" buttons, whereas in the stack situation I would like to just have the navigation bar provided by the UINavigationController.
This would be similar to the Contacts application where the "New Contact" and the "Edit Contact" screens seem to use the same view controller, but the New Contact form is presented modally while the Edit screen is pushed onto the navigation stack.
My question is: What is the best way to handle both situations without having to write 2 separate, but mostly identical view controllers?
I thought about creating a "ModalFormController" which encapsulates the bare "FormController" through composition and adds a toolbar, but I read somewhere in the docs that Apple doesn't recommend nesting view controllers.
Why not use subclassing? Make ModalCreateFormController a subclass of EditFormController and handle the modal-specific stuff in the subclass.
What I do (sometimes) is set up an enum that specifies the type of the view controller.
For example, you might have two types: an Edit type, and an Add ("new") type.
The Add type is implemented via a modal view controller, while the Edit type is pushed onto an existing navigation stack.
In the view controller's -viewDidLoad: method, I simply do a switch/case tree that sets up the title and other appearance characteristics depending on the type enumeration specified above.
The nice thing about this is that it is easy to add a new type. The downside is that the conditional tree for handing this enumeration can get complicated quickly, depending on how different the types are.
But the switch/case tree makes it a lot easier to manage.
So, it depends on what you're trying to do with the two types. But it's definitely doable.
In addition to having an explicit property on the view controller (as Alex Reynolds suggests), two other approaches that occur to me are:
If you have some kind of model object that you're editing, ask it for its current state. If it's ever been saved, then you're in edit mode. Otherwise, you're in create mode.
Look at the value of the controller's parentViewController property. If it's an instance of UINavigationController, then you're in the navigation stack. If you're being displayed modally, it'll be an instance of your list controller.
Ug, I hate extra ivars…
I use this instead:
if([[self.navigationController viewControllers] objectAtIndex:0] == self){
//Modal
}else{
//Pushed
}
It is a bit of a hack, but we are using the logic that if the offending view controller is the first in the stack, you can't go back. Actually we are ignoring the fact of whether it is modally displayed at all.
I had to do this a bunch of times in my app and after trying a couple different ways of doing it, including modal subclasses & a re-usable modal helper classes that used forwardInvocation. I found the best pattern was to make a containingModalViewController method each view controllers that (usually) creates and returns a UINavigationController for the caller to use with presentModalViewController.
In most cases this method builds and returns a UINavigationController with self as the root view controller (with repeated calls to the method checking self.navigationController and returning that instead if it's not nil). Other cases I made a dummy root controller first and pushed self on second in order to get a back button. Then a trick can be used to catch the back button press: http://smallduck.wordpress.com/2010/10/05/intercepting-uinavigationcontroller/
In some cases the view doesn't need a navigation bar and so this method just adjusts some flags and returns self. I even found in some cases that did need a navigation bar it was simpler to make that method invoke self.view, then tweak the view hierarchy to add a UINavigationBar and again return self. But in any case, the setup often isolated to that one method, and the caller handles it the same in each case.
Apple explains how the contacts application works under the hood:
To allow a custom view controller class to be used to both display and edit content, override the setEditing:animated: method.
You get some functionality for free, e.g. Edit/Done button.