View frame changes between viewWillAppear: and viewDidAppear:
Solution 1
Autolayout
made a huge change in how we design and develop the GUI of our views. One of the main differences is that autolayout
doesn't change our view sizes immediately, but only when is triggered, that means at a specific time, but we can force it to recalculate our constraints immediately or mark them as "in need" of layout. It works like -setNeedDisplay
.
The big challenge for me was to understand and accept that, we do not need to use autoresizing masks anymore, and frame has become a useless property in placing our views. We do not need to think about view position anymore, but we should think how we want to see them in a space related to each other.
When we want to mix old autoresizing mask and autolayout is when problems arise. We should think about autolayout implementation really soon and try to avoid to mix the old approach in a view hierarchy based on autolayout.
Is fine to have a container view that uses only autoresizing masks, such as a main view of a view controller, but is better if we do not try to mix.
I never used storyboard, but most proably it is correct. Using Autolayout, frame of your views are set when the autolayout engine starts its calculation. Try to ask the same thing right after super of - (void)viewDidLayoutSubviews
method of your view controller.
This method is called when the autolayout engine has finished to calculate your views' frames.
Solution 2
From the documentation:
viewWillAppear:
Notifies the view controller that its view is about to be added to a view hierarchy.
viewDidAppear:
Notifies the view controller that its view was added to a view hierarchy.
As a result the frames of the subviews aren't yet set in the viewWillAppear
:
The appropriate method to modify your UI before the view is presented to the screen is:
viewDidLayoutSubviews
Notifies the view controller that its view just laid out its subviews.
Solution 3
call
self.scrollView.layoutIfNeeded()
in your viewWillAppear
method. Afterwards you can access it's frame and it will have the same value as you print in viewDidAppear
Solution 4
In my case, moving all frame related methods to
override func viewWillLayoutSubviews()
worked perfectly(I was trying to modify constraints from storyboard).
Related videos on Youtube
Jumhyn
Updated on July 08, 2022Comments
-
Jumhyn almost 2 years
I have discovered a strange behavior in my application, where a connected
IBOutlet
has its connected view's frame between the calls in my view controller toviewWillAppear:
andviewDidAppear:
. Here is the relevant code in myUIViewController
subclass:-(void)viewWillAppear:(BOOL)animated { NSLog(@"%@", self.scrollView); } -(void)viewDidAppear:(BOOL)animated { NSLog(@"%@", self.scrollView); }
and the resulting log output:
MyApp[61880:c07] <UIScrollView: 0x1057eff0; frame = (0 0; 0 0); clipsToBounds = YES; autoresize = TM+BM; gestureRecognizers = <NSArray: 0x10580100>; layer = <CALayer: 0x1057f210>; contentOffset: {0, 0}> MyApp[61880:c07] <UIScrollView: 0x1057eff0; frame = (0 44; 320 416); clipsToBounds = YES; autoresize = TM+BM; gestureRecognizers = <NSArray: 0x10580100>; layer = <CALayer: 0x1057f210>; contentOffset: {0, 0}>
Which clearly shows that the frame is changing between the two calls. I wanted to do setup with the view in the
viewDidLoad
method, but if the content is not available for me to change until it is on the screen, that seems pretty useless. What could be happening?-
Andrea almost 11 yearsAre you using autolayout? are you adding this view in Interface builder or programmatically?
-
Jumhyn almost 11 yearsAutolayout is enabled, and this view is created in IB from a storyboard.
-
Andrea almost 11 yearsI never used storyboard, but most proably it is correct. Using Autolayout frame of your views are set when the autolayout engine starts its calculation. Try to ask the same thing right after super of - (void)viewDidLayoutSubviews mpethod of your view controller.
-
Jumhyn almost 11 yearsThat successfully triggers my event at the right time, but that method is also called whenever I perform any animation on the view.
-
Jumhyn almost 11 years
viewDidLayoutSubviews
was the correct way to go. I just had to put all my content in a subview so that the method wasn't re-called whenever I changed the frame of of the main view. -
Jumhyn almost 11 yearsPost an answer tonight or tomorrow and I will accept ASAP.
-
Kartick Vaddadi about 7 yearsAs of iOS 10, the bounds are correct in viewWillAppear.
-
Jumhyn about 7 yearsAwesome info, thanks!
-
-
Jumhyn almost 11 yearsUgh, that is annoying. Even the wording of the documentation makes it seem as if viewWillApepar: and viewDidAppear: should happen directly after one another.
-
FrizzTheSnail almost 9 years- (void)viewDidLayoutSubviews is the answer for me! Thanks a lot!
-
Rafael Ruiz Muñoz almost 9 years2 months waiting for this answer... thanks I found it !! THANK YOU VERY MUCH!
-
bauerMusic over 8 yearsShould be noted that
viewDidLayoutSubviews
will be called multiple times and not always with the same frame, (I think it's called with CGRectZero on first call sometimes). It gets called for every added subview and other changes in the view. -
solenoid about 8 yearsI have been butting in to this all day (viewDidLayoutSubviews being called multiple times, including when dismissing the view), and just said the ef with it, and put the logic in an if statement and made a bool that gets set to true after the code has run once. I think there has to be a cleaner way, but too much time sunk in to it already.
-
Yaro almost 8 yearswe have to get to the bottom of this! viewDidLayoutSubviews is awful, seNeedDisplay does not work
-
Martin Mlostek almost 8 yearsshould be the checked answer!
-
Fattie about 7 yearsThis answer is really not correct. Yes of course, obviously, for (5?) years now you have to use autolayout. But there are any number of situations (using autolayout) where you need to, say, add something on a screen, "just before it appears to the user". (If you do it in viewDidAppear you'll get a flicker. If you do it in viewWillAppear - the positions will be wrong.) The actual answer is indeed to use viewDidLayoutSubviews.
-
Andrea about 7 years
add something on a screen, "just before it appears to the user". The actual answer is indeed to use viewDidLayoutSubviews.
... you are going to show that view a lot of times -
xaphod almost 7 yearsNo, this is not correct. Example: you have a navigation bar on the screen (from your nav controller). Even after layoutIfNeeded(), the height of the navbar isn't included, so your frame size will change.
-
smakus about 4 yearsThis IS a good way to force a re-calculation of a scrollview frame dimension so that it is consistent throughout the view layout call hierarchy if the scrollView frame is tied to constraints within its superview.