I'm making a custom UIViewController, which is similar to a UITabBarController, as there are some buttons which switch between views. However I'm unsure whats the best way to switch the views:
Have a UIView in the nib file, and add/remove the viewController's views as subviews, as they are needed.
Have a UIView in the nib file (as an IBOutlet), and replace the UIView with the viewController's view so that they are subviews of the myTabBarController's view directly.
Don't have a UIView in the nib, and programmatically set the frame of the viewControllers as they are added, so they are subviews of the myTabBarController's view directly
I had to do something similar once, and in my case it was simpler to have my master
"switching" view (for lack of a better term) maintain a list of UIViewControllers. That way, I was able to maintain the state of the child view controllers even when the corresponding view was not visible or had even been destroyed (to save memory, for example), which made it simpler to keep track of the info on each "page". In my approach, I simply programmatically added each UIViewController to the switch view. Basically your approach #3.
That said, there's nothing wrong with your approaches #1 and #2. They'll do the job. The only thing I don't particularly like about #1 is that it doesn't scale as easily, since you've statically set which views are the children of your switcher at compile time, and cannot easily change that at runtime.
I'm using this approach from Red Artisan's Marcus Crafter. It works remarkably well.
Related
I have created Storyboard with several views calling each other, now I need to create the code
I notice that XCode didn't created .h and .m controller files for each View from storyboard.
Should I create them manually?
Should I keep only one controller? (or few depending of separation of concerns on MVC)
Is there a pattern for developing this?
thanks
The usual approach is one view controller pr. screen full of content. You can imagine having one view controller for a tableview, with any sort of content, and then another view controller that presents that content in a new screen full of content if a row is pressed.
Normally when you have subviews inside of your view controllers, you wire them up in interfacebuilder. Then for instance if you want to populate a view that has a uiimageview and a uiactivityindicatorview inside it, you can control their behavior and how their populated from the view controllers code. You could also if you want something very generic and you feel that one view will probably take up a lot of code in your view controller, create a uiview subclass for it, and then set the class in interface builder.
Did this help? Please let me know if you need more clarification.
It's entirely up to you whether you have a ViewController for each view. If you have many views I would recommend it. Even if you have 2 or 3 views you probably still should. Things can get really confusing when each view has a different task but all have similar IBOutlets.
TLDR; Personally, I would say it was good practice to have a ViewController for each view if each view has a separate task.
I mean this in compliance with OOP and MVC.
Specifically, I've planned out a UIView XIB file that has a lot of dynamically updated UILabels that change every time the user selects something: two timestamps, two durations, and two names. This is a "header" section of the interface that the user is seeing.
I see a few options
1. Create everything in the view controller, add the UIView as a subview to the main view, and keep track of the UILabel subviews with properties. But this forces me to add a lot of properties to the view controller.
2. Subclass UIView and add the UILabels as properties of the UIView, thus keeping the controller "cleaner."
3. Use the XIB file (but let's assume I want to do this programmatically).
I'm asking this because I keep getting the impression that UIView should be subclassed for "custom drawing," and my rationale is more along the lines of "keep my controller cleaner without 6 UILabel properties."
Don't subclass UIView for this. You should subclass uIView for custom drawing or in some cases custom handling for touches (but there are often better ways to handle touches).
Putting everything in your view controller and subclassing UIView are not the only options. You can create a class to manage the group of views that make up the header. It would conceptually be a view controller, but you don't subclass UIViewController, just inherit from NSObject.
In my personal opinion, I would make theUILabels as properties of the UIView, so option 2. You can then access them from the view easily through the controller.
This also gives you the advantage of reusability from the view to any controller, and since dealloc will get rid of those from the UIView, you only worry about cleanup once!
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.
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. :)
At the beginning of my app, I am initializing a UIViewController with a NIB.
At some point, I am removing the view from the screen, then releasing it, so it no longer takes up memory. (At this point I am also saving the state of the view in the UIViewController)
At a later point, I want to recreate the view and bring it back on screen. I will then restore its state. (Using the same UIViewController, not a new one, since it saved the state)
My question, is when I recreate the view, how do I do so from the NIB, or is this not possible?
To me, the obvious remedies are:
Don't save state in the UIViewcontroller (Where does convention dictate that I do save state?)
Don't release the view (maybe just release all of its subviews?)
Don't load my view from a NIB, create programmatically (seems to go against using IB for everything)
Note:
I am not using a UINavigationController, I am handling the swapping of the views myself, since thee are only 2 of them.
If you need that level of control, you dont really want to use IB. Because, as you noted, if remove the view and then later recreate it, you would have to do it in code then anyway. Just design most of the views in IB, then write some code that generates just this view. Then you can call that same method again later to recreate that view when you need it.
You may be able to archive it and later turn it back into an object, but that seems like an inelgant solution. IB does not allow for dynamic creation of controls at runtime, even if they used to exist but don't anymore. There is no shame in leaving IB out of loop for this. In fact it's probably a good idea.
OR
If its a complicated view with a lot of pieces, put the view in it's own nib, and make a view controller for it. Then you can simply instatiate the view controller with the nib name, and add the controllers view as a subview to you main view. Then your view controller handles loading of the nib, and you get to design it in IB. Nothing says the view of a view controller has to take up the entire screen either.
self.otherController = [[OtherController alloc] initWithNibName:#"Other" bundle:nil];
[self.view addSubview:otherController.view];
Don't create the view controller and the view in the same nib file; either create the controller in code or put the view in a separate nib file. If you later nil out your controller's view property, the controller should recreate the view.
But why are you so worried about this? There's already a mechanism to automatically free up the view if you're low on memory: the low memory warning. It will only destroy and recreate the view if you actually need to do so, and it's built in to the system so you don't have to do anything. Remember the saying about premature optimization?