Unable to set frame correctly before viewDidAppear
Solution 1
viewDidLoad is called when the class is loaded however no ui elements have been initialised and therefore any attempt to reference them will be overwritten or unavaliable during the initialisation process which happens between the viewDidLoad and viewDidAppear calls. Once all ui element have been initalised and drawn viewDidAppear is called.
viewDidLoad - Called after the controller's view is loaded into memory
At this point the view isn't within the view hierarchy.
viewWillAppear - Notifies the view controller that its view is about to be added to a view hierarchy.
Again, the view is yet to be added to the view hierarchy.
viewDidAppear - Notifies the view controller that its view was added to a view hierarchy.
Only then is the view added to the view hierarchy.
Update
The viewDidLayoutSubviews
is the most appropriate place to modify the UI before it actually appears on the screen.
viewDidLayoutSubviews - Notifies the view controller that its view just laid out its subviews.
Solution 2
See this thread When is layoutSubviews called?
When use autolayout, framework do not call layoutSubviews automatically. That is very important. From ref:
- init does not cause layoutSubviews to be called (duh)
- addSubview: causes layoutSubviews to be called on the view being added, the view it’s being added to (target view), and all the subviews of the target. ...
If you add subview in viewDidLoad, layoutSubviews called before viewDidAppear, and you can get the correct size of subviews. But if you do nothing, layoutSubviews will be called after viewDidAppear. It's up to your code.
Related videos on Youtube
nacross
Updated on July 09, 2022Comments
-
nacross almost 2 years
I was wondering if anyone knows why when you set the frame of a subview in
viewDidLoad
andviewWillAppear
the changes do not take affect on the screen, but if you set it inviewDidAppear
they do?In my case I am loading a custom xib with two tableviews then attempting to shift them down in
viewDidLoad
to allow space for another view which is added inviewDidLoad
as it is not always necessary to display it.The problem is when i set frame in
viewDidLoad
orviewWillAppear
it is set on the object, i can see by printing it out, but it is not reflected on screen. Moving my set frame calls toviewDidAppear
will cause everything to work as expected.Is it wrong to think I should be able to set the frame in
viewDidLoad
?- (id)init { if ( self = [super initWithNibName:@"MyView" bundle:nil] ) {} return self; } - (void)viewDidLoad { [super viewDidLoad]; self.descriptionWebView = [[[UIWebView alloc] initWithFrame:CGRectMake( 0, 0, self.view.frame.size.width, 200 )] autorelease]; [self.view addSubview:self.descriptionWebView]; self.tableView1.autoresizingMask = UIViewAutoresizingNone; self.tableView2.autoresizingMask = UIViewAutoresizingNone; [self.descriptionWebView loadRequest:[NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"...." withExtension:@"html"]]]; table1LocationWithHeader = CGRectMake( self.tableView1.frame.origin.x, 200, self.tableView1.frame.size.width, self.tableView1.frame.size.height - 200 ); table2LocationWithHeader = CGRectMake( self.tableView2.frame.origin.x, 200, self.tableView2.frame.size.width, self.tableView2.frame.size.height - 200 ); //this will NOT work self.tableView1.frame = table1LocationWithHeader; self.tableView2.frame = table2LocationWithHeader; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; //this will NOT work self.tableView1.frame = table1LocationWithHeader; self.tableView2.frame = table2LocationWithHeader; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; //this WILL work self.tableView1.frame = table1LocationWithHeader; self.tableView2.frame = table2LocationWithHeader; } //I added these after comments for stack overflow users, it seems like viewDidLayoutSubviews is the best place to set the frame - (void)viewWillLayoutSubviews { [super viewWillLayoutSubviews]; //this will NOT work self.tableView1.frame = table1LocationWithHeader; self.tableView2.frame = table2LocationWithHeader; } - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; //this WILL work self.tableView1.frame = table1LocationWithHeader; self.tableView2.frame = table2LocationWithHeader; }
-
Nekto over 11 yearsMay be you could show your code?
-
rob mayoff over 11 yearspossible duplicate of UIViewController returns invalid frame?
-
Maxim Kholyavkin almost 10 yearsNB! Your viewWillAppear call
[super viewDidAppear];
It looks like copy-paste mistake.
-
-
nacross over 11 yearsOkay so viewDidLoad and viewWillAppear are quite simply the wrong place to attempt to modify the frame. Maybe the correct place to set the frame is actually in viewWill/DidLayoutSubviews which i just saw in the question linked to by Rob, as viewDidAppear seems to be too late to modifying the frame. I will try this and see if it works.
-
nacross over 11 yearsI just tried it out and viewDidLayoutSubviews seems like the best place to set the frame. Thanks for helping me clear that up.
-
Sulthan about 11 yearsThis doesn't really explain why setting frame values in
viewDidLoad
doesn't work. I am actually setting the frames inviewDidLoad
very often an never had a problem with it. -
nacross about 11 years@Sulthan I think this problem occurs when you have contraints set in interface builder that conflict with what ever frame you are setting. This must happen between viewWillLayoutSubviews and viewDidLayoutSubviews.
-
Peter over 10 years@Sulthan - I don't usually have problems, but if there is anything that requires the view.frame.size it always gives me the non-iPhone5 (shorter) size, rather than the fullsize one. didlayoutsubviews is the best solution
-
ctlockey over 10 years
viewDidLayoutSubviews
is fantastic! I've been developing for iOS for going on two years now and this is the first I've heard of it. Great answer! +1 -
bauerMusic over 8 yearsI keep seeing this answer where one big issue is being ignored.
viewDidLayoutSubviews
gets called multiple times (for one, on every added subviews as @DienBell explained). If you do many frame calculations there, it will do them multiple times. Far from neat. One can put a flag, but the first call would probably have an incomplete frame. So you really want the last call. But I can't think of a way to accomplish that. Checking thatself.view.frame
is notCGRectZero
can work, but again, far from neat. -
Noitidart about 7 years@bauerMusic - Have you found any better ideas for this? Thanks for sharing that insight.
-
bauerMusic about 7 years@Noitidart No.. I was 'hoping' that Apple will address this one day. I guess that since some frames are dependent on others,
viewDidLayoutSubviews
is the right place, but I mostly use theUIScreen.main.bounds
to get the screen size (which will alway set correctly and does not change). Check my post: stackoverflow.com/questions/33942215/… -
Noitidart about 7 yearsThanks very much @bauerMusic for sharing your update! +1