I know that viewDidLoad method may be called multiple times during UIViewController's lifecycle. But how is that possible? How to make it called more than once not calling it directly? I tried doing it this way:
UIView *view = [[UIView alloc] initWithFrame:self.view.frame];
view.backgroundColor = [UIColor greenColor];
self.view = view;
and whereas my view is actually changed, viewDidLoad is not called. Can anyone give an example?
The first time you access a viewController's view property the view will be created with loadView and then you will receive the viewDidLoad call.
You will not receive the viewDidLoad call again unless the view is destroyed - this may occur if your viewController goes off screen and UIKit decides to purge any view's that are not visible. Thus next time you access the view property it will notice it does not exist and again create one with loadView and then call viewDidLoad.
viewWillAppear method is an UIViewController method. Why you shouldn't call directly?
By the way there is no way to do that, while you assign an UIView to your self.view, id you are not doing it in the init or in the loadView or didLoad methods..
the life cycle is that:
init
loadView //change your view here
viewDidLoad
Then you present the view and:
viewWillAppear:
viewDidAppear:
if you want to change the view during your uiviewcontroller life cycle you should do:
UIView *view = [[UIView alloc] initWithFrame:self.view.frame];
view.backgroundColor = [UIColor greenColor];
[self viewWillAppear:NO]; //set to yes if you are making some kind of animation
self.view = view;
[self viewDidAppear:NO];
The will disappear and did disappear will be called according to the UIVIewController life cycle.
Related
I wanted to know how to make a viewcontroller without the use of xib or storyboard. So in this case, I would be getting the viewcontroller as such
PopupViewController* vc = [[PopupViewController alloc] init];
[self addChildViewController:vc];
[self.view addSubview:vc.view];
[vc didMoveToParentViewController:self];
I know it has to do something with overriding the init method since we are not using initWithCoder ?
I know I have to create a view and set it as the self.view for the PopupViewController, but I was wondering how I could do that.
EDIT: Yes it may be much easier just to make an xib file or add a view controller to the storyboard, this is to understand deeply how view controllers work.
The best place to init the view is in PopupViewController's loadView method. Something like:
- (void)loadView {
self.view = [MyViewClass new];
}
Then, in MyViewClass initWithFrame: method build all subviews and set constraints to it. If you're not using constraints, override layoutSubviews method.
I have my view controller class MyVC extending from UIViewController class. In the designated initializer I change the background color to GREEN as following
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
[self.view setBackgroundColor:[UIColor greenColor]];
}
return self;
}
I also have the loadView method that creates a new UIView object and changes its color to RED
- (void)loadView
{
UIView* view = [[UIView alloc]initWithFrame:[[UIScreen mainScreen] bounds]];
[view setBackgroundColor:[UIColor redColor]];
[self setView:view];
[view release];
}
The designated initializer is called before loadView call. So I expect that my view color (which I set GREEN in designated initializer) should become RED (which I did in loadView).
I see my color GREEN and if I comment that GREEN color line in designated initializer, then I see the RED color. So why is it not overriding the view properties in loadView method if it is called after initializer?
Caleb has it almost right. When you access a view controller's view property, the view accessor method checks whether the view has been loaded yet. If not, it calls loadView, then viewDidLoad, then returns the view.
This line in your initializer accesses the view property:
[self.view setBackgroundColor:[UIColor greenColor]];
So to return the view, the view accessor calls your loadView method. Your loadView method sets the view's background color to red. Then your initializer sets the background color to green.
If you sprinkle some NSLogs in your initializer and your loadView method, or if you put a breakpoint in your loadView method, you will see that loadView is called from view, which is called from initWithNibName:bundle:.
The purpose of -loadView is to, uh, load the view. It's called when you access the view controller's view property and the value of that property is nil. In this case, you're accessing self.view in your initializer, so that's when -loadView gets called. You set the view's background after that happens, so the view ends up with a green background.
I don't understand the mechanism of loadView: function (this function is in UIView).
I created a project as below:
First, I created a iPhone's window-based project.
Then, I created a UIView subclass
Next, I created a UIViewController subclass, with no xib.
Lastly, in the loadView: function of the class I created in the third step, I designate the UIView object (in the class I created in the second step) as the view variable of the UIViewController object (in the third step).
If I omit the last step, and place the statement NSLog(#"test LoadView"); in the loadView: function, then when the project is run, the statement NSLog(#"test LoadView"); is invoked continuously, result in the run is overflow.
Please explain me! Thank you!
loadView: is only invoked when the view property is nil. Use this when creating views programmatically. default: create a UIView object with no subviews. For ex -
- (void)loadView
{
UIView *view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
[view setBackgroundColor:color];
self.view = view;
[view release];
}
By implementing the loadView: method, you hook into the default memory management behavior. If memory is low, a view controller may receive the didReceiveMemoryWarning message. The default implementation checks to see if the view is in use. If its view is not in the view hierarchy and the view controller implements the loadView: method, its view is released. Later when the view is needed, the loadView: method is invoked again to create the view.
Not sure why you want to use loadView: but you can do just as much in viewDidLoad:
Reference -
Why is this iPhone program not calling -loadView?
loadView
Hope this helps.
I'm pretty new to iOS development, and I'm trying to develop a simple app in which a button changes the subviews. I have a base RootViewController, which displays MiddleView correctly on init. MiddleView has a single button, labeled "First," which is connected (in Interface Builder) to RootViewController's -openFirstView.
Here's how MiddleView is displayed within RootViewController's -viewDidLoad
MiddleViewController *middleTemp = [[MiddleViewController alloc] initWithNibName:#"MiddleView" bundle:nil];
self.middle = middleTemp;
self.middle.rootViewController = self;
[self.view addSubview:middle.view];
[middleTemp release];
So I have the following ViewControllerss: MiddleViewController and FirstController which control MiddleView and FirstView respectively, and a RootViewController which switches between the two.
I've linked this by placing a RootViewController reference in MiddleViewController, and adding
self.middle.rootViewController = self;
to RootViewController's -viewDidLoad.
-(IBAction)openFirstView:(id)sender{
[middle.view removeFromSuperview];
[self.view addSubview:firstController.view];
}
Note: I've tried initializing firstController within -openFirstView, and when it initially didn't run, I moved the initialization to -viewDidLoad and have proven that it is initializing from the nib correctly by displaying FirstView directly in -viewDidLoad
Where firstController is loaded to a reference earlier in code. However, when I run the code and click the button, nothing in the view changes.
I've done some more diagnosing. I've found specifically that -ViewDidLoad in rootViewController is being called twice, once on the original load and once on the first click of the button, and I'm not sure exactly why.
It seems you haven't inialize your firstView in the below method
-(IBAction)openFirstView:(id)sender{
[middle.view removeFromSuperview];
//First initialize the firstController
[self.view addSubview:firstController.view];
}
When working with views and view controllers in an iPhone app, can anyone explain the difference between loadView and viewDidLoad?
My personal context, is that I build all my views from code, I do not and will not use Interface Builder, should that make any difference.
I've found that often when I add init code to loadView, I end up with an infinite stack trace, so I typically do all my child-view building in viewDidLoad...but it's really unclear to me when each gets executed, and what is the more appropriate place to put init code. What would be perfect, is a simple diagram of the initialization calls.
Thanks!
I can guess what might be the problem here, because I've done it:
I've found that often when I add init code to loadView, I end up with an infinite stack trace
Don't read self.view in -loadView. Only set it, don't get it.
The self.view property accessor calls -loadView if the view isn't currently loaded. There's your infinite recursion.
The usual way to build the view programmatically in -loadView, as demonstrated in Apple's pre-Interface-Builder examples, is more like this:
UIView *view = [[UIView alloc] init...];
...
[view addSubview:whatever];
[view addSubview:whatever2];
...
self.view = view;
[view release];
And I don't blame you for not using IB. I've stuck with this method for all of Instapaper and find myself much more comfortable with it than dealing with IB's complexities, interface quirks, and unexpected behind-the-scenes behavior.
loadView is the method in UIViewController that will actually load up the view and assign it to the view property. This is also the location that a subclass of UIViewController would override if you wanted to programatically set up the view property.
viewDidLoad is the method that is called once the view has been loaded. This is called after loadView is called. It is a place where you can override and insert code that does further initial setup of the view once it has been loaded.
viewDidLoad()
is to be used when you load your view from a NIB and want to perform any customization after launch
LoadView()
is to be used when you want to create your view programmatically (without the use of Interface Builder)
Just adding some code examples to demonstrate what NilObject said:
- (void)loadView
{
// create and configure the table view
myTableView = [[UITableView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame] style:UITableViewStyleGrouped];
myTableView.delegate = self;
myTableView.dataSource = self;
myTableView.scrollEnabled = NO;
self.view = myTableView;
self.view.autoresizesSubviews = YES;
}
- (void)viewDidLoad
{
self.title = #"Create group";
// Right menu bar button is to Save
UIBarButtonItem *saveButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Save" style:UIBarButtonItemStyleDone target:self action:#selector(save)];
self.navigationItem.rightBarButtonItem = saveButtonItem;
[saveButtonItem release];
}
To prevent an infinite loop from happening when you read self.view, call the class' super implementation when you load a view. The super implementation will allocate a new UIView for you.
- (void) loadView {
[super loadview];
// init code here...
[self.view addSubView:mySubview1]; //etc..
}
The easiest way to use loadView is to make some type of base view controller, like MyBaseViewController which is subclass of UIViewController. In it's loadView method create view in this way:
-(void) loadView {
if ([self viewFromNib]) {
self.view = [self viewFromNib];
} else {
self.view = [[[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
}
self.view.autoresizingMask = UIViewAutoresizingFlexibleHeight;
self.view.backgroundColor = [UIColor whiteColor];
}
And when you need to make some view controller you just use subclass of MyBaseViewController and in it's loadView controller you just call [super loadView] like this
//sucblass loadView
-(void) loadView {
[super loadView];
//rest of code like this..
UILabel *myLabel = [[UILabel alloc] initWithFrame:myFrame];
[self.view addSubview:myLabel];
[myLabel release];
}
loadView() is called when your controller is asked to create its self.view. You can do it by yourself like
self.view = [UIView alloc] init...];
Or your controller's parent UIController class has already a method name -loadView() which initializes your self.view into blank view. Then you can call
[super loadView];
I really recommend the second approach as it encourages the inheritance. Only if your view controller is not directly inherited from UIViewController.
The definition given by Apple on viewDidLoad mentioned that it is called after the controller’s view is loaded into memory. To put it in a simple term, it is the first method that will load.
You might be thinking under what condition will this method being fully utilized? The answer is, basically whatever you wanted the app to load first. For instance, you might want a different background color, instead of white, you could perhaps choose blue.