UIViewController vs. UIView - which one should create subviews? - iphone

I'm trying to wrap my head around the roles of UIViews and UIViewControllers. If I'm creating and inserting subviews programmatically, is it typical to do this from the view or the controller?
I'm really just wondering if there's a convention/pattern for this. In my sample application, I'm loading 50 images at runtime, adding them as subviews to the main view, then letting the user drag them around the screen. I'm currently doing all the initialization in the view's initWithCoder:
- (id)initWithCoder:(NSCoder*)coder
{
if (self = [super initWithCoder:coder]) {
// load UIImageViews and add them to the subview
}
return self;
}
The view also implements touchesBegan/touchesMoved to allow dragging. My problem comes when I try to access [self frame].size in initWithCoder, it doesn't seem to be initialized yet. This makes me think I may be loading the images in the wrong place...

It depends on what you're doing. If the view represents something "packaged", then you create subviews from the view itself. If you're merely aggregating views together, then you should do it from the view controller.
Think about traditional encapsulation. Is your subview conceptually "part" of its superview, or does it just live there in the drawing hierarchy?

Suppose you're creating a view that contains some controls, like to edit a record with multiple fields or something. Since you're going to be using those text fields and their data in the view controller, they conceptually "belong" to the view controller, and the parent view exists just for grouping and layout purposes. This is probably the more common scenario.
If you were creating a view subclass to behave like a new control, for example, then I would say you should create and manage the views entirely within the view class. I imagine this case will happen less frequently in practice.

It sounds like you may be adding them in the wrong place. If you moved the addition of the subviews into the ViewController, then you could do this work on viewDidLoad and you'd be guaranteed that the main view was already initialized and ready to access.

Dummy explanation would be like this:
Subclass UIViewController whenever you plan to display the view as modal (presentModalViewController) or inside UINavigationController (pushViewController)
Subclass UIView whenever it is a small component inside main view... for example you wish to do custom button or ...
More specifically in your example ... if you are initializing UIViewControllers you can set view's properties eg. like frame size only after your view is added to superview. Only
at that point UIViewController loads and initializes its view.
I think in your case you should use UIViews ... you can set frame size immediately after allocating&initializing them.

Related

Where is the best place for... [iPhone]

In UIViewControllers I have several methods such as viewDidLoad, viewWillAppear, viewDidAppear, etc.
The question is: "what is the best place to, for example, set the background color of my view, instantiate, and set the background color of a UILabel, or instantiate an object that I set as #property in my class and things like that? "
Thanks!
So many questions at once!
The first time the view is loaded, in viewDidLoad you will already have access to all the readily initialized UI elements, so it is a great place to configure the view and to set your class properties.
If you come back to the view if it is already loaded (say, by popping a view from a navigation controller, or dismissing a modal view controller), viewDidLoad will not be called. Thus, if you want to change something (background, add a label, change the background of a label, etc.) based on something that might have happened since the view controller was initialized, you have to use viewWillAppear.
You would use viewDidAppear if you want to animate a change so the user can see it after the view has already become visible.
Edit: this is pertinent for if you use IB or storyboard. See CitronEvanescent's answer for the case that you create your view in code.
The feasible method would be viewDidLoad or -(id)init,-(id)initWithNibName constructors of the class.
viewWillAppear and viewDidAppear should be avoided,since you would not like to instantiate your variables again and again since they get called every time the view appears(from pop or tabSwitch).These two methods can be useful in case you want to change some variable values on reappearance
If you are creating your view programaticaly, you can set your properties on - (void) loadView this method will be call once before anything is displayed.
Generally i prefer instantiating the UI elements in the -(id) init and update their UI in loadView.
For further details : View construction reference

iPhone - a UIView that behaves like a UIViewController

I need to create a class that will present a UIVIew and has a some code to initialize it before it is ready to show. I need to know when the view is ready, I mean, I need something like viewDidLoad or viewWillAppear, but as it is a UIVIew it lacks these protocols.
I cannot implement it as a UIViewController as I don't want to present it modal. It is really a rectangular view that needs to show on a screen side.
How do I declare this class? If the class is a UIView based I don't have the methods I mentioned.
thanks
Any reason to not do that kind of stuff inside the initWithFrame method on a UIView? Also, you can do additional stuff on layoutSubviews. A view controller has viewDidLoad because the view is lazy loaded (from a nib or otherwise - it also has a loadView). It has viewWillAppear and viewWillDisappear because it is managing the view (btw, even the view controller is managed by another view controller - these methods are called when you have the controller within a UINavigationController or UITabBarController or such classes which mange UIViewControllers. - the view itself is not really managing anything. All it knows about is how to draw itself. For that, you have layoutSubViews, drawRect, etc.
Doing some heavy stuff upon view's load will definitely kill the UI performance. You probably need to implement another kind of design pattern that will asynchrounously assign data values to the instance of your custom view - when that is done, you call layoutSubviews or setNeedsDisplay to update the view.
The scenario you described is no reason for not implementing a UIViewController. Assume you have a container view A and a subview B. Both have their own UIViewController AC and BC. Now on AC you add the View B managed by BC:
[self.view addSubview:BC.view];
You probably want to be using UIViewController.
You don't just have to present them modally. You can get your view controller's view with yourViewController.view, and add that as a subview of whatever view you want.
If you're targeting iOS 5, there are a few new methods (such as addChildViewController:) designed to make doing things like this easier. You can do it on iOS 4 too though, and it'll still work.
Implement a drawRect in youR UIView, plus an initialization flag. Just before the view is to be displayed the drawRect will be called. If the initialization flag isn't yet set, do your initialization and set the flag. This will only look good if your initialization can be done quickly (no long synchronous calls).
I need to create a class that will present a UIVIew and has a some code to initialize it before it is ready to show. I need to know when the view is ready, I mean, I need something like viewDidLoad or viewWillAppear, but as it is a UIVIew it lacks these protocols.
You might want to rethink how your view is being used. It sounds like you're trying to put too much controller-like logic into your view. That's why you're wanting your view to behave like a controller.
More specifically: What exactly are you trying to accomplish? If you're waiting for data to load before display the view, that might actually be something to put in the controller that is calling the view.
To illustrate my point, imagine you're putting some text in a UILabel that you read from disk. The reading from disk isn't really related to the view. The view only cares what text it displays, not how it received the text. Once it's read from disk, you can create a UILabel with that text that you read. This allows the UILabel to be more flexible.
That example might not be at all related to what you're doing, but I use it as an example of the difference between a view and a controller. Anything not related with the display and drawing of the view shouldn't belong there.

Iphone UIView switching: Previous view's subviews polluting newly switched-to view

I've been working for a while now on switching UIViews, and am trying a basic UIView switch, without using UINavigationController. I've looked into UINavigationControllers, but I have been wrestling with the below for so long that I want to explore it fully.
My project organisation is:
MainViewController.h/.m -> viewDidLoad: This method runs a NSTimer to update self.view with a few bits and bobs.
ConfigView.h/.m: -> viewDidLoad: nothing - just an idle view.
MainViewController.xib -> Main window NIB
ConfigView.xib -> ConfigView NIB
I have the following method in MainViewController.m execute on a UIButton press event:
- (IBAction)switchToConfigView:(id)sender {
if(self.configViewController == nil) {
ConfigView *cv = [[ConfigView alloc] initWithNibName:#"ConfigView" bundle:nil];
self.configViewController = cv;
[cv release];
}
[self.view addSubview:configViewController.view];
}
Now, when I switch to the ConfigView, the view is indeed displayed, but it appears that viewDidLoad: from MainViewController still executes, causing both views to effectively be merged. From the code, I can see that this is obviously the case, as the actual view switch itself is performed within the MainViewController context, and as you can see, simply a subview.
My question is, how do I neatly tuck away/pause all goings-on within MainViewController when switching the view by just adding another subview to it? My guess is that it's not possible, or rather it's a lot of leg work where some other methodology would be better applied. I suspect I need to implement the actual switch from the ConfigView.h/.m but I could be wrong.
Any light you can shed on the above would be grand.
thanks
swisscheese.
My suggestion is:
1) have a base, empty view;
2) add a subview to it; this would be your actual first view;
3) when you want to switch, remove the first subview from the base view and add the second one as a subview;
4) when you want to switch back, remove the second one and add the first one as a subview to the base view;
you can easily handle as many view as you like. it works.
alternatively, you can hide the view instead of removing it.
Believe me adding a whole new sub-view on another view is really a massive task and later on when the project becomes big it becomes a painful task to switch views.
This is my own personal experience as I did this big mistake in one of my previous project.
In all cases navigation control is the best choice for switching views rather than adding sub-views.
Please tell me that what difficulties are you facing in Navigation control and I can help about this.
Thanks,
If the NSTimer you've set up in MainViewController's viewDidLoad method is adding subview's to mainViewController's view with the addSubview method, those views will be placed on top of all other subviews, i.e. on top of the config view. So, you might try either invalidating the timer when presenting the config view.
You should really just use a UINavigationController, or present the config view controller with presentModalViewControllerAnimated. Why don't you want to?

Making an alternative to UITabBarController

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.

Do I programmatically add SubViews in ViewDidAppear, ViewDidLoad, ViewWillAppear, the constructor?

I'm trying to figure out from Apple's sketchy documentation which method is the best place to be initializing and adding my Views controls to the controller's view.
With winforms it's fairly straightforward, as they're always initialized inside InitializeDesigner, called in the constructor. I'm trying to match the reliability of this pattern if possible.
I'm working with UIViewControllers and UITableViewControllers inside a UINavigationController most of the time - if this effects it all.
Here's an example:
public MyController()
{
// Here?
AddViews();
}
public override ViewDidLoad()
{
base.ViewDidLoad();
// Or is should it be here?
AddViews();
}
public override ViewWillAppear(bool )
{
base.ViewWillAppear(animated);
// Here?
AddViews();
}
public override ViewDidAppear(bool animated)
{
base.ViewDidLoad(animated);
// Or maybe here?
AddViews();
}
void AddViews()
{
UILabel label = new UILabel();
label.Text = "Test";
label.Frame = new RectangleF(100,100,100,26);
View.AddSubView(label);
UIWebView webview = new UIWebView();
webview .Frame = new RectangleF(100,100,100,26);
View.AddSubView(webview);
}
I get mixed results with some UIControls when I add them to the view in different places. Visual lag sometimes, othertimes the webview is hidden somewhere.
Is there a general rule to keep to for adding them?
In general, this is what I do:
ViewDidLoad - Whenever I'm adding controls to a view that should appear together with the view, right away, I put it in the ViewDidLoad method. Basically this method is called whenever the view was loaded into memory. So for example, if my view is a form with 3 labels, I would add the labels here; the view will never exist without those forms.
ViewWillAppear: I use ViewWillAppear usually just to update the data on the form. So, for the example above, I would use this to actually load the data from my domain into the form. Creation of UIViews is fairly expensive, and you should avoid as much as possible doing that on the ViewWillAppear method, becuase when this gets called, it means that the iPhone is already ready to show the UIView to the user, and anything heavy you do here will impact performance in a very visible manner (like animations being delayed, etc).
ViewDidAppear: Finally, I use the ViewDidAppear to start off new threads to things that would take a long time to execute, like for example doing a webservice call to get extra data for the form above.The good thing is that because the view already exists and is being displayed to the user, you can show a nice "Waiting" message to the user while you get the data.
There are other tricks you can use, though. Lets say you want a UILabel to "fly" into the form after the form is loaded. In that case, I would add the label to the form in the ViewDidLoad but with a Frame outside of the view area, and then in the ViewDidAppear I would do the animation to fly it back into view.
Hope it helps.
Hmm, Apple's docs seem to be pretty clear, IMHO.
If you create your own root view (the root view of this particular controller's view hierarchy) programmatically, you should create it in -loadView without calling super and set the view property when done. If your view is loaded from a nib, you should not touch -loadView.
You add custom subviews to the view controller's view or otherwise modify it in -viewDidLoad. The recommended practice is to create your UILabel and UIWebView in -viewDidLoad and release them in -viewDidUnload, setting their references to nil if you need to keep them in ivars.
Note: -viewDidUnload is deprecated in iOS 6 and is just not called any more, because UIViewController no longer purges its view under memory pressure.
viewDidLoad relates to 'MEMORY', and viewWillAppear/viewDidAppear relates to 'APPEARANCE'. A view controller's view (which is a root-view of your view controller's views) can appear/disappear numerous times, even if the controller's view is already in memory.
(When referring to root-view, I also means its subviews, because the root-view has reference to its children (subviews), but from a view controller's perspective, it usually knows just the root-view. Reference to subviews can happens normally through view controller's outlets.)
The root-view itself MAY be removed from memory when there's a memory warning. The view controller will determine when is the best time to removed them from memory.
So, you would normally add subviews in viewDidLoad, because adding subviews means adding them to memory. BUT not if you create all of your views programmatically (not from a nib files). If that's the case then you should override loadView method, and create a root-view and add subviews there, so in this case you can omit viewDidLoad to add subviews.