Wrong Frame Size in Viewdidload

ios10: viewDidLoad frame width/height not initialized correctly

The most common issues you describe are appearing in iOS 10 only and can be solved by adding this line (if necessary):

self.view.layoutIfNeeded()

just above the code, that is responsible for changing constraint, layer.cornerRadius etc.

OR

place your code related to frames / layers into viewDidLayoutSubviews() method:

override func viewDidLayoutSubviews() {

super.viewDidLayoutSubviews()
view.layer.cornerRadius = self.myView.frame.size.width/2
view.clipsToBounds = true

... etc
}

view.frame.width IS correct in viewDidLoad with storyboard, why?

No you still can't use viewDidLoad for anything size/position related.

The first method that will have the actual "proper" dimensions is the viewDidLayoutSubviews.

Regarding the phenomenon you are experiencing.

You are probably talking about the "root view" from your view controller.

As the apple docs say (bolding is mine):

A view controller’s root view is always sized to fit its assigned
space
. For other views in your view hierarchy, use Interface Builder
to specify the Auto Layout constraints that govern how each view is
positioned and sized within its superview’s bounds. You can also
create constraints programmatically and add them to your views at
appropriate times. For more information about how to create
constraints, see Auto Layout Guide.

https://developer.apple.com/documentation/uikit/uiviewcontroller

This is why you are getting the correct width.


If you'd like to test, try adding a subview that fills the whole "root view" area. This one will probably just give you the dimensions that that are shown on the Interface Builder.

My test results:

Interface Builder -> "View as iPhone 7"

Simulator iPhone 5s:

Root View dimensions: (0.0, 0.0, 320.0, 568.0)
Sub View dimensions: (0.0, 0.0, 375.0, 667.0)

Simulator iPhone 7:

Root View dimensions: (0.0, 0.0, 375.0, 667.0)
Sub View dimensions: (0.0, 0.0, 375.0, 667.0)

Simulator iPhone 7 Plus:

Root View dimensions: (0.0, 0.0, 414.0, 736.0)
Sub View dimensions: (0.0, 0.0, 375.0, 667.0)

Interface Builder -> "View as iPhone 4s"

Simulator iPhone 5s:

Root View dimensions: (0.0, 0.0, 320.0, 568.0)
Sub View dimensions: (0.0, 0.0, 320.0, 480.0)

Simulator iPhone 7:

Root View dimensions: (0.0, 0.0, 375.0, 667.0)
Sub View dimensions: (0.0, 0.0, 320.0, 480.0)

Simulator iPhone 7 Plus:

Root View dimensions: (0.0, 0.0, 414.0, 736.0)
Sub View dimensions: (0.0, 0.0, 320.0, 480.0)

As you can see, the root view dimensions will always correspond to the device you are using as stated in the documents. While any other views will have the dimensions you can see on the interface builder.


This question will also help you understand how the life cycle works:
Why am I having to manually set my view's frame in viewDidLoad?

Autolayout: Incorrect frame sizes in ViewDidAppear

I solved my own question.

I constrain views proportional to VC's height.

The VC that I'm displaying has a Navigation Bar. All the view sizes are correct,(even in viewDidLoad because I call layoutIfNeeded()), until the VC starts drawing the Navigation Bar and shrinks the Superview's height; thus, shrinking its subview's heights.

All I have to do is to Extend edges under the top bar by ticking the appropriate box in the VC's Attributes Inspector. This way VC will not shrink the superview of my subviews.

UIViewController viewDidLoad incorrect width/height

If you are targetting iOS 5.0 or better you can use viewWillLayoutSubviews and viewDidLayoutSubviews to make changes.

As for your second question, if you need access to an instance variable in other method than init, you need to keep it around, I don't see a problem with it.

You can, however, try to use Auto Layouts and set up rules between the subviews so it's automatically laid out for you without the need to keep a reference.

iOS incorrect frame size at runtime

viewDidLoad is too early. At this time, the views have the frames they were given in the storyboard. Ditto for viewWillAppear.

In viewWillLayoutSubviews, the view controller's top-level view has its correct frame, but its descendants do not.

In viewDidLayoutSubviews, the view controller's immediate subviews have their correct frames, but more distant descendants (“grandchildren” and so forth) don't.

If the image view is a direct subview of the view controller's view, then its frame is up to date in viewDidLayoutSubviews.

If the image view is a more distant descendant, then there is no method you can override in the view controller that will be called after the image view's frame has been updated but before the image view is visible on screen. Here are two options in this case:

  1. Create a custom subclass of UIView to be the superview of the image view. When the superview's layoutSubviews runs, after it calls super.layoutSubviews, the image view's frame is up to date.

  2. Create a hidden UIView that is a direct subview of the view controller's top-level view. Use constraints to make this hidden view's frame exactly match the image view's frame. Hidden views participate in layout, so when viewDidLayoutSubviews is called, this hidden view's frame is up to date, and is the same as the image view's frame will eventually be (except that the hidden view's frame is in the top-level view's geometry, which might be different than the geometry of the image view's superview).

Why am I having to manually set my view's frame in viewDidLoad?

The frame is not guaranteed to be the same in viewDidLoad as it will be when the view is eventually displayed. UIKit adjusts the frame of your view controller's view prior to displaying it, based on the context in which will appear. The size is determined based on interface orientation and the dimensions of any visible navigation bar, tab bar, toolbar, or status bar (which itself has a height that can change, e.g. when you're on a phone call).

It helps to understand what happens when a view controller's view is loaded and displayed:

  1. Something accesses your view controller's view property for the first time. This may occur in your own code, or in UIKit in response to a user action like selecting a tab.

  2. UIKit lazy-loads your view controller's view by calling loadView if it's defined, or by loading the view from the NIB that was specified in initWithNibName:bundle:. If neither exists, UIKit just loads an empty view.

  3. UIKit calls viewDidLoad once the view and its subviews have been fully loaded. At this point the view's frame will be whatever it was set to in the NIB, or in loadView.

  4. Something calls for UIKit to display your view controller's view. Again, this may be a user action like tapping on a tab, or an explicit method call in your code like pushViewController:animated: or presentModalViewController:animated:.

  5. UIKit resizes the view based on the context in which it will be presented, as described above.

  6. UIKit calls viewWillAppear:. The frame should now be the size that will be displayed. (?) EDIT: This may no longer be true. See the comments below.

  7. UIKit displays the view, with or without animations.

  8. UIKit calls viewDidAppear:.

As you can see, if you need to know the size of your view's frame before it gets presented, viewWillAppear: is your one and only opportunity. Just remember that this size may change after the view appears for various reasons, including rotation events or changes in status bar height. For this reason, it's important to give every subview an appropriate autoresizingMask, to ensure that the layout can adjust itself properly for any change in bounds.

If you wish to build your view hierarchy manually, the recommended place to do so is in loadView. Since you construct the view yourself in this method, you can initialize its frame to whatever you'd like. The size you choose doesn't matter much, since UIKit is likely to change it on you anyway. Just make sure you set your autoresizingMasks appropriately.



Related Topics



Leave a reply



Submit