I'm trying to get a better understanding of how Container Views work in the storyboard. The behaviour appears to be that the container view will force its subview to resize to fill the container.
I see no constraints that explain it, and there's no mention of what class it is. It seems like some storyboard magic.
I assume that the container view must be a subclass of UIView
, and I'll have a wild guess and assume its called UIContainerView
, but searching through the documentation yields only two results.
So how does it work?
The storyboard editor (Interface Builder) keeps the embedded view's frame
set to the container view's bounds
during editing. Thus when the storyboard is written to a file, the serialized sizes of the views are identical. This happens whether or not the storyboard has auto layout enabled.
The top-level view of each view controller in the storyboard also has its autoresizing mask set to UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight
, again regardless of whether the storyboard has auto layout enabled.
If auto layout is enabled, each top-level view has its translatesAutoresizingMaskToConstraints
set to YES
. This is different than all of the descendants of those top-level views. All of the descendants have translatesAutoresizingMaskToConstraints
set to NO
.
The embedding relationship is represented as a segue of class UIStoryboardEmbedSegue
. (This is a private class, not part of the public API.)
When UIStoryboardEmbedSegue
receives the perform
message, it loads the destination view controller's view and adds it as a subview of the container view. Then it sets the embedded view's autoresizingMask
to UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight
. This is redundant, since Interface Builder already set it that way in the storyboard.
Then -[UIStoryboardEmbedSegue perform]
checks the embedded view's translatesAutoresizingMaskToConstraints
. This is also redundant, because Interface Builder set it to YES
.
If the embedded view's translatesAutoresizingMaskToConstraints
is YES
, perform
sets the embedded view's frame
to the container view's bounds
. Again, redundant.
If the embedded view's translatesAutoresizingMaskToConstraints
is NO
, perform
adds constraints H:|[childView]|
and V:|[childView]|
, thus forcing the embedded view to fill the container view. (Yes, it actually uses the visual format language.) This branch should not be reached.
When a view has translatesAutoresizingMaskToConstraints
set to YES
, auto layout automatically adds constraints of type NSAutoresizingMaskLayoutConstraint
and keeps them up to date when you change the view's frame
. For example, the root view of a window is made to fill the window using autoresizing constraints:
<NSAutoresizingMaskLayoutConstraint:0x7555d00 h=-&- v=-&- UIView:0x7671780.midX == UIWindow:0x7551010.midX>,
<NSAutoresizingMaskLayoutConstraint:0x7555de0 h=-&- v=-&- UIView:0x7671780.width == UIWindow:0x7551010.width>,
<NSAutoresizingMaskLayoutConstraint:0x7555eb0 h=-&- v=-&- UIView:0x7671780.midY == UIWindow:0x7551010.midY + 10>,
<NSAutoresizingMaskLayoutConstraint:0x7555ef0 h=-&- v=-&- UIView:0x7671780.height == UIWindow:0x7551010.height - 20>
So that's what “causes Container View to keep its child view's frame to match its bounds”.
I figured this out by looking at the .storyboard
file (it's surprisingly readable XML), and by looking at -[UIStoryboardEmbedSegue perform]
in Hopper.
As for why they have the redundant checks, I can think of a few likely reasons:
IB (perhaps in pre-release versions) didn't always set up the view properties the way it does now, so the code isn't redundant when loading old storyboards.
Apple has internal tools that generate storyboards differently than IB.
The code is there for forward compatibility with future versions of IB that allow top-level storyboard views to have different properties.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With