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:
Create a custom subclass of
UIView
to be the superview of the image view. When the superview'slayoutSubviews
runs, after it callssuper.layoutSubviews
, the image view's frame is up to date.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 whenviewDidLayoutSubviews
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:
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.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 ininitWithNibName:bundle:
. If neither exists, UIKit just loads an empty view.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 inloadView
.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:
orpresentModalViewController:animated:
.UIKit resizes the view based on the context in which it will be presented, as described above.
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.UIKit displays the view, with or without animations.
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
Why Can't I Invert My Image Back to Original with Cifilter in My Swift iOS App
Get Random Child from Firebase Database
iOS Video Compression Swift iOS 8 Corrupt Video File
Implementing Fast and Efficient Core Data Import on iOS 5
Xcode 10: a Valid Provisioning Profile for This Executable Was Not Found
Images Can't Contain Alpha Channels or Transparencies
Dashed Line Border Around Uiview
Custom Cell Row Height Setting in Storyboard Is Not Responding
Uiimageview Missing Images in Launch Screen on Device
iOS Autolayout: Two Buttons of Equal Width, Side by Side
Uiview Vertical Flip Animation
How to Dismiss a Storyboard Popover
Creating iPhone Pop-Up Menu Similar to Mail App Menu
Uicollectionview - Horizontal Scroll, Horizontal Layout
How to Detect Whether Custom Keyboard Is Activated from the Keyboard's Container App
How to Disable No Inverse Relationship Warning for Coredata in Xcode 4.2
Detect If App Is Running in Slide Over or Split View Mode in iOS 9