Handling Your Initial View Controllers for iPhone

Programming starts with a

design

A lot of webpages and articles talk about how to program, but they don’t talk about how to design a program. Good program design separates the weekend programmers from the professionals. Sure, anyone can piece together a working application but a good design allows a power and flexibility of adding new features, for example, without having to recode half of your application.

Here, we’ll talk about handling your initial views when your iPhone application loads. This might be a simple task if your application is simple, but take the case of having a welcome screen followed by a login screen followed by a tabbed view controller. You might set it up so that one activates the other upon being dismissed itself.

Here’s an example of how the process might look:

Program loads AppDelegate

Load Welcome view from AppDelegate

Dismiss Welcome view -> Tell Welcome view to load Login view

Dismiss Login view -> Tell Login view to load Tabbed view

What if you had to add a view or remove one, say if the login was no longer needed if you gave your users the option to save their credentials. You’d have to rewrite two different view controllers at least. And this is just a simple example so you can see how complicated this chain of views would get once your application grows. For most programmers this way is fine, and sadly enough most of the time, this is the way it’s done. But true programmers are lazy. Lazy programmers are good programmers. So be lazy and write good code so that when (not if) you go back and edit your code, it’ll be a snap.

A design starts on paper

Your programs should be conceived on paper. Even with applications that attempt to do this, I still find paper the ultimate programming tool (even though I did the one below on a computer).

Class Diagram
Class Diagram

First off, your controllers should be logically separated based on their tasks that they need to accomplish (not based on how the user will view them). So, you should have a controller or controllers to handle logging in and session creation and have appropriate UIViews and UIViewControllers to get and display info to your user. This may not be a one-to-one relationship though. For example, you may not have a UIViewController that’s associated with creating your sessions at all.

Next you will have your application’s business logic view controllers, controllers and all that jazz. The controllers sit independently to the view controllers except, of course, where there’s coupling or interaction. They are not tied directly, or rely upon any particular view. You illustrate those connections on paper (or in this case in the example diagram above). So if your “Add New Event” feature, let’s say, needs access to the session data, draw a line from your SessionController to your EventsController with an arrow pointing in the direction of the flow (from EventsController to SessionController in our case since the Events controller needs to know about the SessionController but not vise versa).

Abstraction solves everything

Since your controller code should be independent of how your views are laid out, you won’t have your AddNewEventViewController (the view that lets the user add an event) handling the actual creation and saving of events but will instead gathering and packaging the information taken from the user and given to an EventsController to handle the manipulation. Who knows, you may have several views that edit an Event in one way or another and if you change something in how the Event is saved, you’ll have to go into the code of each one that touches Event and rewrite code — exactly what we’re trying to avoid.

Abstracting the data in this way is called indirection and “All problems in computer science can be solved by another level of indirection;” — David Wheeler, except as Kevlin Henney says, “…except for the problem of too many layers of indirection.”

Singletons, under used

and under appreciated

So, you should have a session controller that sits somewhere ready to be called by who ever needs to validate a session or login (those views that would need access could be your tabbed views, could be your app delegate when your app starts, or could be in your settings views where you type your login info or when you log out). This is usually accomplished with a singleton class where any controller can access that one (and only) instance of your controller without having to pass around references to that controller. It not only cleans up your code, but also helps data integrity by not allowing multiple objects to edit the same data.

Handling your initial views

You should really have a separate controller handle organizing your views rather than the app delegate since the app delegate should really only be used to initialize your app and handle delegation of your application, not controlling business logic. So the structure should look like:

AppDelegate -> MainViewController -> LoginViewController

I like to have an overall MainViewController that handles all the subviews, be it a tab bar view controller, a table view controller, or whatever your main view is going to be. This level of indirection allows you to easily change what your main view controller is later, should your requirements or ideas change. Having said that, the MainViewController is going to need to get and maintain a reference to the application’s window.

To actually add the views, stack them on top of each other. Add the Tab Bar Controller’s view to the Window’s view first, then if needed, add your login views after that to stack them on top, and then remove them as needed when you’re done with them. By removing them from the top, it reveals the bottom view.

In MainViewController’s implementation file, your viewDidLoad: will look like:

[code lang=”objc”]
– (void)viewDidLoad
{
[super viewDidLoad];
[mainWindow addSubview: tabBarController.view];
[mainWindow addSubview: loginViewController.view];
[mainWindow addSubview: welcomeViewController.view];
}
[/code]

Where mainWindow is an IBOutlet to your Window in your MainWindow.xib file (or you could get it programatically if you prefer). Also, loginViewController is a pointer to your view controller that you put on top of the tab bar controller. On top of your login view, put a welcome view displaying any text, graphics or video to show after your application loads; note that this is different from your Default.png initial image. Then when you’re done with your login view, call removeFromSuperview on them to remove them from the view stack. So something like this in your MainViewController (since it handles all manipulation of your main views):

[code lang=”objc”]
– (void)removeWelcomeView
{
[welcomeViewController.view removeFromSuperview];
// Any other code you need to clean up after your welcome view is removed
}
– (void)removeLoginView
{
[loginViewController.view removeFromSuperview];
// Any other code you need to clean up after your login view is removed
}
[/code]

Obviously, when you remove the welcome screen view, under it (since you added it on the stack before the welcome view) is your login view. Then, removing the login view will show the tab bar view since that was added first in your view stack:

(Top)

Welcome View

Login View

Tab Bar View

This way we’re loading all of our views at once and stacking them so the one on top hides the one below. This has a slight overhead since you’re adding all your views when your applications starts but you can always delay adding the tab bar controller to the Window’s view until all the initial views are removed, for example. Then, if you’d like, using the MainViewController, you can add views on top of the tab bar view during execution, for example if you need to login again.

There are a few different ways to handle multiple views and each case is different. This is just one idea, with a few general guidelines. Feel free to ask any questions in the comments if anything wasn’t clear enough.

Sample Code

The following sample code is provided as is for educational and demonstration purposes. I threw it together really quickly and is only used to demonstrate the use of a MainViewController. Normally, you’ll want to make your MainViewController a singleton class, but in this example I passed the MainViewController instance to the subviews in order for them to call different methods on MainViewController to change the current views. By using a singleton design, you can avoid passing around this reference since there is only ever one MainViewController.

Also, this code demonstrates how to create a tab bar programmatically. It needs to be the root view of your window, but that doesn’t mean it needs to be the root view controller. We are still allowed to use a MainViewController as our root to handle all subviews and their controllers.

Download sample code (2010-02-11_TabBarTest.zip)

23 Comments on “Handling Your Initial View Controllers for iPhone

  1. Hello Mike,

    Interesting article. Especially for someone like me, who just started learning to develop on the iPhone. Does this mean that you instantiate all your ViewControllers at once from within the MainViewController? Doesn’t that eat up a lot of memory?

    At the moment I have started developing an application for the iPhone. The application has a LoginView (with its LoginVIewController). This ViewController is instantiated from the appDelegate (in the applicationDidFinishLaunching method).
    So if login happens successfully, I will work from there and load the views as needed.

    But somehow using “[window addSubview:loginViewController.view];” doesn’t load the LoginView and I keep seeing MainWIndow.xib. Any idea how that could be?

    Regards,
    Vahid

    • Thanks for the comment Vahid,

      You *can* instantiate all of your views at once if you want, depending upon your needs. However, this model allows you to be flexible. Just create methods to handle view instantiation and removal in your MainViewController and you can decide when views get created. For example, if you have a function in your MainViewController like removeLoginView: you can make that method remove the loginView and then instantiate a new view and then add that to your window’s view. In other words, when you call removeLoginView, only then will it create your next view, delaying the overhead of having many views in memory. But depending on your secondary view, you may want a longer application load time (by loading all your views at once in the beginning) than a delay after logging in (if you were to create the view after login, which might take some time). So really, it just depends on when loading that view makes sense for your particular case.

      By default, your iPhone application will load the MainWindow.xib. There should be a default view in there if you open it in Interface Builder. If you want to add your own view programmatically, simply delete that view in IB and then call your [window addSubview:loginViewController.view] method you mentioned. Also make sure you have a [window makeKeyAndVisible]; method after adding the new view.

      I hope that helps clear up my article for you. Let me know if anything else wasn’t clear and I’d be more than happy to elaborate.

      Thanks!
      Mike

  2. Thanks for the explanation.

    So basically all that MainViewController does is just instantiate the Views, it’s up to you when you really create them, right?

    About the MainWindow.xib. If i open that in IB it seems it doesn’t have a View but a Window(UIWindow) only. I see no default view to be honest.

    Also in your example it seems that all the above ViewControllers still need to be added to the mainWindow (MainWIndow.xib). So you still depend on the MainWIndow.xib, this meaning that the MainWIndow is still the parent of all other ViewControllers.
    But I assume this has to be the case if you create all your views at once? Instead of creating them as you go?

    Let me use this as an example:
    AppDelegate –> LoginView –> CreateCustomerView –> CustomerCreatedView

    So AppDelegate knows only (and instantiates) LoginView, LoginView knows only (and instantiates) CreateCustomerView etc.
    So only the parent view knows about it’s child view, and there is no ‘all-knowing’ app delegate. Is this also possible? and could this be a negative or positive way of designing your application?

    Regards,
    Vahid

    • Yea, thanks for reminding me. I created a basic example a few weeks ago but forgot to post it. Check out the end of this article under Sample Code to see the basic idea put together.

      Thanks again!

  3. thanks for the sample code it works great… i was trying to do the same in my code but after i added the MainViewController my app just shuts down im prob missing some steps in the interface builder side. could you give more info about how everything is linked in MainWindow.xib

  4. This is a very informative article and like so many I’ve absorbed shows there is flexibility in how you can write a great iphone program

    Have you done anything with view switching using removeFromSuperView and no buttons? I’m working on an app and am completely stuck.

    cj

    • If you have a reference to the view, you can call it’s removeFromSuperView method. You can put that inside another method that gets called automatically, either by a timer or by some other action. What are you stuck on specifically?

  5. Very nice, and helpful – this was exactly the information I have been searching for! Thanks SO much!

  6. Hello Mike

    Thanks for the wonderful tutorial. After lots of search. I ended up in yours and it makes sense your way of programming. I have been off IT for sometime and just getting back with iPhone. Got get rid of my passion. Two things. I downloaded your sample code and figured it out, how it works. I see it little bit different from your description above. However I figured out how i works.

    One part, I am not able to figure out is. After login, I am able to show my tabbar. But On relaunch, It is going back to tab bar, but I want to show the login screen again. What do I do? Any help will do.

    Thanks and Good Day!
    Boju

    • Thanks for the comment. The likely reason you’re seeing the tab bar is because the app hasn’t fully quit. With the new iOS 4, multi-tasking is standard and the app might appear to be quitting, but you need to explicitly quit in order to see the login again.

    • Storyboards affect it a lot. In the method I used, I usually add my Views and ViewControllers programmatically. I actually don’t do a whole lot in Interface Builder unless it’s an actual and complex interface. If you want to use the new Storyboards method, you can setup your storyboard to handle the different view controllers, depending on the organization of your storyboards. However, that’s up to you and you can always choose to do anything programmatically to get things up and running and then switch over or vice versa.

  7. thanks for this great tutorial. I managed to have the loginView / tabBArcontroller switch in my apps but I was sure I did not do it in a right way. Stacking the views makes things a lot clearer.

    One thing I’m still not confortable with is the rootViewController object, I do not really understand when you said (talking about the tabBarcontroller) “It needs to be the root view of your window, but that doesn’t mean it needs to be the root view controller.”. Would do you mean ?

    thanks, that’s great stuff,
    Luc

    • The Window object itself is the one and only Window in your application. The Window needs a root view to begin displaying content and maintains a pointer to its root view. The tab bar controller’s view needs to be that root view to work properly, in some cases. However, your Root View Controller class can set the Window’s view to any other view it has access to. So, you use your root view controller to handle what view becomes the Window’s view and swap it out according to your needs.

      Great question! I hope that cleared it up for you.

      • Thanks Mike, that clarifies things.

        In my current iOS app project, I am doing the switch of views (login / tabbar) in the app’s delegate, I know that is not clean and that really bugs me 🙂
        Reading your article I started changing this to use a MainViewController object because your approach is really clean (thanks).

        I have a problem though as I do not see my views anymore (black screen) when is app is ran, there is a link I probably missed somewhere 🙂

        I do not have any MainWindow.xib file so I’ve done the linking programmatically (pretty user this is this part causing problem). I’ve written the main stuff in this gist: https://gist.github.com/1744267

        Would you have any idea of what is missing ?
        Thanks a lot for your help.

        Luc

        • Hello again,

          I think I figured it out, I had a bad UIWindow / MainViewController as I though. But do not hesitate to tell if you see something strange in the gist 🙂

          Cheers,
          Luc

    • A sign up view is just another view on the stack of other views. For usability reasons, I would include it as a subview or subcontroller of the login view. This is because it’s likely a new user would not be logged in, thus shown the login view. The login view would then have a link to a signup view. The login view would thus be responsible for login and signup. I would probably break this out into a UserAccountController that will handle communication with the server to handle login/authentication, signup and any other account tasks you need to handle. Under the UserAccountController you could have your ViewControllers to handle display and control of the appropriate views (Login, Signup, Edit Account).

      I hope that made sense. Thanks for the comment!

  8. hello Mike
    thanks for this great artikel.
    I am new in iOS and try to understand some point, can you help me to find my way.
    The point about mainwindew.xib, i have project like this:
    main window ( with 3 button ) as start window —–> subview (when button click).
    Must i add uiview as rootviewcontrollar ( the 3 button on it ) Or i can add this button on mainwindow.xib? what is the best way using mainwindow.xib as main view or add uivew as main view? and how?