I'm tinkering with an iPad app that (like many iPad apps) doesn't use the UINavigation root view control system, so I don't have natural ownership for each app "view". I essentially have two basic views: a document list view, and a document edit view.
I'm playing with UIView animation for getting from a selected document to the edit view.
I also have a toolbar on top that exists (with different buttons) in both "views".
Because I don't have UINavigation running the show for me, I have a tendency to just throw more and more stuff into one NIB and one view controller that owns the whole container. But now I'm trying to figure out how to segue from the document list view to the edit view if the edit view lives inside a different NIB, preserving the toolbar too.
Anyone have thoughts or experience on app structures like this? I find the docs lacking on best practices around code/UI structure for anything except trivial one-screen apps or full-on navigation apps.
You're not "supposed" to have parent/child view controllers owning subcomponents of the same "screen" according to the docs, but this implies one massive honking view controller that basically contains the whole app, and that can't be right.
Not sure if there's a "right answer" to this; I'm looking for some intelligent examples or suggestions. Nobody's touched this question in months, so I'm adding a bounty to generate good chatter. :)
Thanks!
UPDATE: I'm not talking about a split view, which is clearly well handled by a split view controller. Instead, take a look at Apple's iWork apps (e.g. Pages) which have a document list view and an independent edit view, but these are related by animation.
Maybe the real question here is: how would you (or could you even?) build a "container" view controller like the split view or navigation controller, yourself? Are you required to build the whole damn thing from scratch? I sense that you are, because seems to be hidden wiring in the interaction between view controllers. If so, thoughts on this?
I think the only hidden wiring in view controllers is setting parentViewController, which is required to support the categories declared for split and navigation.
View controllers are designed to be nested, with each controller owning a part of the view hierarchy. The only design goal is that no view controller reach into the view hierarchy of another controller. A parent view controller will usually have some call for adding child controllers so that it can set the frame of the view within the view hierarchy it owns. A child view controller should not do anything to the superview of the view it controls, as that is owned by another controller. Not should it set the center or frame of the view it controls.
For example, a navigation controller has a push method, in which it removes the previous controller view, adds the new controller view, and sets the frame of the newly added view. In general, a parent view controller is free to set the frame of the view of a child controller, but not the bounds.
If you want to change the animation of a navigation controller, I think you would start by implementing every method with an animated: argument. Set up your animation then call super with the animated flag off before committing the animation.
I haven't tried the multiple-view-controllers thing outside of the UIKit-provided ones (navigation/tab-bar/modal/etc) but I don't see why it shouldn't work. Under the hood, everything is a view, though I'll note that UIKit has special views for view controllers which no doubt have some kind of special handling by the system (UIViewController has a wrapper view, UINavigationController has a UINavigationTransitionView or something, ...).
I'd advise not worrying too much about "best practice" — just code something which does what you want. A couple of options:
Stick logic into view classes (ewwww!). One of our apps does this so it can handle rotation in a custom way (views slide in/out instead of rotating). We should've probably implemented our own view controller logic instead.
Break the model into components which correspond to views. Make each view know how to load its component and recursively load subcomponents. Then the view controller only needs to worry about "big picture" stuff.
Also note that you can load multiple nibs into the same view controller by e.g. connecting it to an outlet called editView.The big difference is that it's not done automatically by -[UIViewController loadView] so you need to do something like
-(EditView*)editView {
if (!editView) {
// Loads EditView into the outlet editView
[NSBundle loadNibNamed:#"EditView" owner:self];
}
return editView;
}
You'll also need to worry about adding it to your view hierarchy, unloading it on -(void)viewDidUnload (iPhone OS 3.0+), setting it up in -(void)viewDidLoad in case there was a memory warning during editing mode, etc...
It's not easy, but UI never is.
You need a master-detail view implemented with a split-view/popover-view and controlled with a UISplitViewController.
The master view is the document list and the detail view is the edit view. Each has its own controller. The two controllers themselves are managed by the UISplitViewController.
If you don't use the splitview controller you will just end up hand coding something that works very much like it. This is really the only way to do what you want easily in the API and it is the layout that iPad users will expect.
See Creating a Split View Interface in the iPad programming guide for details.
Related
In an iPad application I have a tab Controller containing several view controllers. One of these view Controller (call it MainViewController) needs 2 table views side by side.
So I wrote 2 UITableViewController subclasses and from MainViewController, I alloc/init both subclasses of UITableViewController, and add the tableview from each to the MainViewController's view.
This means that UITableViewController subclasses's views are subviews of MainViewController's view.
This answer: https://stackoverflow.com/a/7684648/191463 says that doing that is incorrect and it seems Apple are starting to cut down on it.
I really do not want to have to put all the code from both UITableViewControllers in MainViewController, as it will make it much harder to read and in future could create duplicate code, if I want to use one of the tableview elsewhere in the app by itself.
Is this actually a problem, if it is how do I do it properly?
Apple isn't cutting down on it. This is the only way to create custom container view controllers prior to iOS 5. Apple actually listened to the developers and made it easier to do this sort of thing in iOS 5 with child and parent view controller methods, not to mention they made it so it worked hierarchically.
In most cases, this wouldn't actually be a problem in terms of applications crashing or performance or anything. It can be a problem in some cases, because let's say you have a child view controller. You add the view controller's view to your root view controller. Prior to iOS 5, child view controllers were things like navigation controller view controllers, tab bar controller view controllers, and modal view controllers. What happens when you have a button that calls [self.parentViewController dismissModalViewControllerAnimated:YES];? Technically, the view controller isn't being presented as a modal view controller, you added the view to the root view controller view.
In iOS5, you're able to add child view controllers to view controllers and are able to transition from one child view controller to another.
Now even if your view controller doesn't have a different parent, adding a "root" view controller to another root view controller isn't the best way to do it (especially since you don't get access to the parent view controller unless you explicitly create a parentViewController pointer in the child view controller). So in the end, Apple just made it easier and more decoupled.
It is OK to do it so long you take the responsibility of managing the viewController life cycle events
initWithNibName...
loadView:
viewDidLoad:...
.
.
viewDidUnload..
dealloc
memoryWarnings
orientation changes
So if you create a custom "container view controller" it becomes your responsibility to call all these methods on child viewControllers at the appropriate time. Think of it as "If you were to implement UITabBarController" what all will you have to manage regarding the children ??"
It quickly gets complex. Adding another viewController's view as subview is childs play.
iOS 5 does some of this stuff for you by specifying parent child relationship, however I still haven't seen any sample code anywhere yet to point to.
I'd say it is not incorrect or wrong to create view controller containers, especially when Apple engineers do that themselves. UITabBarController, UINavigationController or UISplitViewController - they are all view controller containers. More over many great apps with unique UX do that more common than you think. However the real issue is that it's quite hard to do it the right way, so e.g. view lifecycle, memory management and rotation handling is done properly along the hierarchy of views. Fortunately Apple guys did a decent job and iOS5 introduced lots of functionalities regarding controller containers:
UIViewController class reference
View Controller Programming Guide for iOS
If you're interested how above problems had to be addressed before iOS5, read these two very good blog posts:
Writing high-quality view controller containers
Using view controllers wisely
The View controller programming guide states this regarding view controller's usage:
Each custom view controller object you
create is responsible for managing all
of the views in a single view
hierarchy. In iPhone applications, the
views in a view hierarchy
traditionally cover the entire screen,
but in iPad applications they may
cover only a portion of the screen.
The one-to-one correspondence between
a view controller and the views in its
view hierarchy is the key design
consideration. You should not use
multiple custom view controllers to
manage different portions of the same
view hierarchy. Similarly, you
should not use a single custom view
controller object to manage multiple
screens worth of content.
I understand that if we use multiple custom view controller's to control the parts of a view (i.e. a view controller to manage subViews of a main view which in turn is managed by a view controller) the default methods like:
didReceiveMemoryWarnings
viewWillAppear
viewWillDisappear
viewDidUnload
etc. etc. will not be called.
Apart from this, is there any other solid reason why we should not be using multiple view controllers to manage the respective subviews of a view?
The documentation also provide an alternative solution which reads as:
Note: If you want to divide a view
hierarchy into multiple subareas and
manage each one separately, use
generic controller objects (custom
objects descending from NSObject)
instead of view controller objects to
manage each subarea. Then use a single
view controller object to manage the
generic controller objects.
But there is no mention as to why multiple view controllers should not be preferred. My question is:
Why should not we prefer it this way?
I am concerned because I prefer using UIViewController's subclass to manage my views since I load them from nib each time and I segregate nibs for each view controllers. It becomes easy to cater the changes in later stages of the project. Is this wrong? Should I necessarily change my programming style, or is it ok if I go ahead with this approach?
Thanks,
Raj
Well, I'd say "as long as it works", you can keep on doing like you do !
But to keep things "cleaner", I'd use my own objects.
Since ViewControllers are designed with other general features in mind (like working with navigation controllers and tab bar controllers), which makes it a bit "heavy" for a simple usage, like you do.
Plus, like you mentioned, some events are only called when the viewController's view is added to the main window.
Can't you use your own objects with Interface Builder ? If you create one (or several) UIView IBOutlet(s), it should work the same.
I have an app that does use two UIViewControllers on a single screen. The child is a UITableViewController. I don't rely on any of the UIViewController behavior of the child -- only the UITableViewController methods. This is convenient because there are other cases where the child UITableViewController does manage the whole screen. And in that case, it does use the UIViewController methods. Questionable design? Maybe. It has worked fine for two years now. But I'm not sure I would recommend the pattern.
I have always sort of wondered when to use a UIView vs. a UIViewController on the iPhone.
I understand that you shouldn't use a UIViewController unless it's a full-screen view, but what other guidelines are there?
For example, I want to build a modal overlay - a screen that will slide into place over the current screen. If this modal overlay is full-screen, should it be a UIViewController? The last time I built something like this, I subclassed UIViewController, but now I wonder if that was correct.
From Apple's View Controller Programming Guide for iOS:
"The most important role of a view controller is to manage a hierarchy of views. Every view controller has a single root view that encloses all of the view controller’s content. To that root view, you add the views you need to display your content."
Also:
"There are two types of view controllers:
Content view controllers manage a discrete piece of your app’s content and are the main type of view controller that you create.
Container view controllers collect information from other view controllers (known as child view controllers) and present it in a way that facilitates navigation or presents the content of those view controllers differently.
Most apps are a mixture of both types of view controllers."
This is a great question.
My basic rule of thumb. Is that every major 'page' of an application gets it's own view controller. What I mean by that is that during the wire framing stage of application design, everything that exists as its own entity will eventually be managed by its own View Controller. If there is a modal screen that slides over an existing screen, I will consider that to be a separate 'page' and give it its own view controller. If there is a view that overlays and existing page (such as a loading screen or help popup.) I would treat those differently, implement them as UIView subclasses and keep the logic in that 'pages' view controller. It the popup has behavior I will communicate back to that pages View Controller using the delegate pattern.
I hope this helps. It is very much a philosophical and architectural question and much could be written about it.
I use UIViewController whenever a view is full screen and either has outlets/actions and/or subviews.
Put everything on a screen into a UIViewController until the view controller starts to have too much code, then break out the screen into multiple UIViewControllers contained by one master view controller...
To put that into context of your answer, make a view controller for that modal overlay. It will have one anyway if you are using a nav controller to present it (and you probably should).
I have a somewhat different approach:
Override UIView if you plan to do custom drawing in drawRect. Otherwise, subclass UIViewController and use [self.view addSubview: blah] to add the components of the page.
There are a few other special cases, but that handles about 95% of the situations.
(You still will often need a UIViewController with a custom UIView. But it's common to have a custom UIViewController with no corresponding custom UIView.)
Is the thing that slides in a self contained screen? I mean, does it directly interact with the parent? If so, make it a UIView, if not, probably recommend a UIViewController.
A UIView is part of the UIViewController see the view property of UIViewController for this. As you pointed out correctly UIViewController manages a complete screen and there should be only one visible UIViewController at a time. But in most cases you will have more UIViews or subclasses of UIView visible on the screen.
The example you gave would be a correct use in most cases. As you may have noticed you will get a lot of functionality when subclassing the UIViewController. Animating the appearance and dismissal of the UIViewController would be one of them.
As marcc pointed out if the thing you want to slide in is not a self contained screen you would be better off using a UIView.
As a conclusion I would say that if you want to use the functionality that comes with subclassing UIViewController than go for it make it a UIViewController. Otherwise a UIView might be better.
The itunes U Standford class has a great lecture on UIViewControllers I would recommend watching it, because it has a lot of information regarding UIViewControllers in general.
If you are familiar with the MVC pattern, then you should be able to understand the difference between UIVIew and UIViewController. To make a simple statement, UIView is for rendering UI elements on screen. UIView is the superclass of pretty much all Cocoa Touch UI elements. Those elements do not know what information they are supposed to display, what they should do when a user clicks a button, what happens when an async network request is completed and so on. UIViewController is for all that and more. The view controller is responsible for placing the UI elements in the correct locations on screen, setting the contents of the UI elements, handling button presses and other user inputs, updating the model when needed etc.
Conceptually, a single UIViewController controls the contents of the whole screen in an iPhone App and that is why it is often easy to think of things in terms of view controllers. If you need a view where the user can select ingredients for a food recipe, you'll need a UIViewController for that. I made this distinction for myself because coming from a Java background I wasn't used to the framework enforcing MVC. I would think of things in terms of UIViews, and start implementing them that way and then run into all sorts of trouble because of that. If you are going to stick to UIKit for your App, then the workflow Apple has made for you is: for each separate view in your App, create a UIViewController subclass and then use Interface Builder to place the UI elements and to create connections for buttons etc. It works wonders, saves a ton of time and lets you concentrate on making your App function well.
I use UIViewController for showing View on full Screen.
For better control on custom view I prefer subclass of UIViewController instead of UIView, earlier I was using UIView for making custom sub class.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I have just started developing iPhone applications and I am very confused by how the "view controller" aspect of the user interface works.
I did the "Your First iPhone Application" tutorial on the Dev Center. It has you set up your own view controller class and then initialize it using initWithNibName. So it seems that nib files contain view controllers. But it's also possible to have a nib file that just has a view, not a view controller. For example if you set up a TabBarController and then navigate to any tab other than the first one, there will be a gray box that says "view" and if you double click that you get to set up a view to go in that tab (but it's just a view, not a view controller, am I right?) So are views subclasses of view controllers or vice versa?
Another thing I am having trouble understanding is nested view controllers. The way I understand that you use a view controller (at least from the tutorial) is that you create your own custom view controller (or is it actually a view controller? In the tutorial I don't see where myViewController is actually declared to extend UIViewController) that has all the delegate methods in it, and then use initWithNibName to load the existing view controller into the custom view controller. (Right so far?) So suppose I create a nib file with a TabBarController at the root, and of course each tab will have a root view controller. So then I loadWithNibName the file and stick it in my own root view controller. Now how do I get access to all the "internal" view controllers so that I can assign delegate methods to them? Or is the recommended option to make the root view controller the delegate for both its own view and the views of all the subsidiary view controllers?
Here's another example. I am planning to have a TabBarController where for some of the tabs, the view controller for that tab will be a NavigationController. The way I understand navigation controllers is that you have to programmatically push a view on top of the stack when you want to drill down in the hierarchy. Suppose the view I pushed on is a view I originally created in Interface Builder (and loaded in using initWithNibName.) But of course the space to display the view is smaller than the space available for the view when I created it (because when I created it it was on a blank slate, while when I display it there's a navigation bar and a tab bar using up some of the screen.) So do the view controllers automatically resize the view to compensate? (IIRC, part of the documentation did mention auto-resizing somehow, but it seems like since the aspect ratio changes, scaling to the right size would leave the text looking "squashed".)
Finally is there some tutorial or explanation somewhere that explains clearly how the view controllers work? That also might help me answer my questions.
Docs on view controllers and learning related stuff
(1) Apple's UIViewController reference. Short and sweet (relatively).
(2) View Controller Programming Guide for iPhone OS. More wordy.
(3) Not view controller specific, but still: Cocoa Dev Central. Education with pretty pictures.
Nested View Controllers
The fact is that there are some key points that are a tad glossed over in the introductory docs. And I think a lot of the confusion arises from using the tab bar controller and navigation controller in particular.
For example:
You should not use view controllers to manage views that fill only a part of their window—that is, only part of the area defined by the application content rectangle. If you want to have an interface composed of several smaller views, embed them all in a single root view and manage that view with your view controller.
-UIViewController class reference
So, that certainly makes it sound like you should never nest view controllers, right? Well, from my experience, what they meant to say was something more like never nest view controllers, except maybe in a tab bar controller, or in a navigation controller, or in a navigation controller in a tab bar controller. But other than that, never nest view controllers.
View resizing
Another good question you raise: Is the coder responsible for sizing the view, or is that done by a view controller? And the usual answer is: yes, the coder is responsible for all layout and exact sizing of view elements -- except when that view is the first one added to a tab bar or navigation controller -- they tend to be somewhat aggressive about sizing your views for you. There are more details to know - things like autoresizing your view if the orientation changes - but this is a good rule of thumb to start with.
Views vs view controllers (and models)
Neither views nor view controllers are subclasses of each other. They're independent dudes. Generally, a view controller is more in charge, and tells the view what data to display, or where to position itself, etc. Views don't really tell controllers what do to, except perhaps to inform them of a user action, such as a UIButton calling a method in its controller when it gets pushed.
Here's a quick analogy to help understand the whole MVC model. Imagine you're building the UI for a car. You need to build a dashboard to display information to the driver - this is the view. You need sensors to know how fast it's going, how much gas you have, and what radio station is on - this data and direct data-handling stuff are like model objects. Finally, you need a system which knows when to turn on the low-on-gas light, or to turn off your turn signal when you turn the wheel far enough, and all the logic like that - this is the controller component.
Other stuff
A nib file is mostly a way to save view-related data that would make for ugly code. You can think of them logically as freeze-dried class instances, ready to use. For example, positioning a grid of 20 buttons that will be the UI for a calculator; a bunch of pixel coordinates makes for boring code, and is a lot easier to work with in interface builder. It can also accommodate view controllers because that code is also occasionally very boilerplate - such as when you need a standard, everyday tab bar controller.
About which view controllers control which views: Again, the tab bar and navigation controllers are kind of special cases, as they can act as containers for other view controllers. By default, you think of having just one view controller in charge of the full screen at a time, and in that case, there's no question that this one view controller is handling any delegated calls back from your view or other elements. If you do have some container view controllers, then it still makes sense for the most-specific view controller (the most nested one) to be doing all the dirty work, at least for the views which are not controlled by the container controllers (i.e. anything not the tab bar / navigation bar). This is because the container view controllers aren't usually considered as knowing about what they contain, and this agnosticism gives us better decoupled, and more maintainable code.
And, yes, in this context a view controller is always meant to be a subclass of UIViewController.
View controllers are simply that - objects that handle control of a view.
A XIB files doesn't "contain" a view controller. Instead it normally tells a XIB, what view controller it will be wired up to eventually - that's what the initWithNib call is doing, creating a view controller, getting the view out of the xib, and then attaching the view controller to where the XIB says it should connect to parts of the view.
There are nested view controllers technically when you use a navigation controller or tab bar, but your own view controller basically gets called as if it were the top level because those containers understand they will be holding other view controllers.
As for resizing - it's not a pixel resize, it's a container resize. The view controller resizes the master view it's hooked up to, then the auto-resizing behavior for any elements that view holds determines how they are resized - some things like lables might shift around, but by default do not shrink. If you click on the Ruler tab in IB you can see the current autoresize behavior for any object in a view - the center lines with arrows at both ends tell you if an object will allow resizing, and in which directions. The lines on the outside of the square tell you what side(s) the object will "stick" to, meaning the object will keep the same distance from those edges no matter how the container holding it resizes.
I'm not sure what the best book for IB is, but you probably cannot go wrong with a good fundamental Cocoa book which should explain autoresize behaviors.
I have a view that contains a UITableView and a UILabel which works perfectly as far as I can tell. I really don't want to manage the UIView and UITableView with the same controller as the UITableViewController handles a lot of housekeeping and according to the documentation:
If the view to be managed is a
composite view in which a table view
is one of multiple subviews, you must
use a custom subclass of
UIViewController to manage the table
view (and other views). Do not use a
UITableViewController object because
this controller class sizes the table
view to fill the screen between the
navigation bar and the tab bar (if
either are present).
Why does Apple warn against using it and what will happen if I ignore this warning?
Update: Originally I quoted the following from the Apple Documentation:
You should not use view
controllers to manage views that fill
only a part of their window—that is,
only part of the area defined by the
application content rectangle. If you
want to have an interface composed of
several smaller views, embed them all
in a single root view and manage that
view with your view controller.
While this issue is probably related to why UITableViewController was designed to be fullscreen, it isn't exactly the same issue.
The major practical reason to use only one view controller per screen is because that is the only way to manage navigation.
For example, suppose you have screen that has two separate view controllers and you load it with the navigation controller. Which of the two view controllers do you push and how do you load and reference the second one? (Not to mention the overhead of coordinating the two separate controllers simultaneously.)
I don't think using a single custom controller is a big of a hassle as you think.
Remember, there is no need for the TableviewDataSource and the TableViewDelegate to be in the actual controller. The Apple templates just do that for convenience. You can put the methods implementing both protocol in one class or separate them each into there own class. Then you simply link them up with the table in your custom controller. That way, all the custom controller has to do is manage the frame of tableview itself. All the configuration and data management will be in separate and self-contained objects. The custom control can easily message them if you need data from the other UI elements.
This kind of flexibility, customization and encapsulation is why the delegate design pattern is used in the first place. You can customize the heck out of anything without having to create one monster class that does everything. Instead, you just pop in a delegate module and go.
Edit01: Response to comment
If I understand your layout correctly, your problem is that the UITableViewController is hardwired to set the table to fill the available view. Most of the time the tableview is the top view itself and that works. The main function of the UITableViewController is to position the table so if you're using a non-standard layout, you don't need it. You can just use a generic view controller and let the nib set the table's frame (or do it programmatically). Like I said, its easy to think that the delegate and datasource methods have to be in the controller but they don't. You should just get rid of the tableViewController all together because it serves no purpose in your particular design.
To me, the important detail in Apple's documentation is that they advise you not to use "view controllers [i.e., instances of UIViewController or its subclasses] to manage views that fill only a part of their window". There is nothing wrong with using several (custom) controllers for non-fullscreen views, they just should not be UIViewController objects.
UIViewController expects that its view takes up the entire screen and if it doesn't, you might get strange results. The view controller resizes the view to fit the window (minus navigation bars and toolbars) when it appears, it manages device orientation (which is hard to apply correctly if its view does not take up the entire screen) etc. So given how UIViewController works, I think there is merit to Apple's advice.
However, that doesn't mean that you can't write your own controller classes to manage your subviews. Besides the things I mentioned above, interacting with tab bar and navigation controllers, and receiving memory warnings, there isn't really much that UIViewController does. You could just write your custom controller class (subclassed from NSObject), instantiate it in your "normal" fullscreen view controller and let it handle the interaction with your view.
The only problem I see is the responder chain. A view controller is part of the responder chain so that touch events that your views don't handle get forwarded to the view controller. As I see it, there is no easy way to place your custom controller in the responder chain. I don't know if this is relevant for you. If you can manage interaction with your view with the target-action mechanism, it wouldn't matter.
I have an application where I did use 2 separate UIViewController subclasses below another view controller to manage a table view and a toolbar. It 'kind of' works, but I got myself into a massive pickle as a result and now realize that I should not be using UIViewController subclasses for the sub controllers because they contain behavior that I don't need and that gets in the way.
The sort of things that went wrong, tended to be:
Strange resizing of the views when coming back from sub navigation and geometry calculations being different between viewWillLoad and viewDidLoad etc.
Difficulty in handling low memory warnings when I freed the subview controllers when I shouldn't have done.
Its the expectation that UIViewController subclasses won't be used like this, and the way they handle events, using the navigation controller etc that made trying to use more than one UIViewController subclass for the same page tricky because you end up spending more time circumventing their behaviour in this context.
In my opinion, The Apple Way is to provide you the "one" solution. This served the end-users very well. No choice, no headache.
We are programmers and we want to and need to customize. However, in some cases, Apple still doesn't want us to do too many changes. For example, the height of tab bar, tool bar and nav bar, some default sizes of the UI components(table view), some default behaviors, etc.. And when designing a framework and a suite of APIs, they need to nail down some decisions. Even if it's a very good and flexible design, there is always one programmer in the world wants to do something different and find it difficult to achieve against the design.
In short, you want a table view and a label on the same screen, but they don't think so. :)