Autoresizing is a matter of conceptually assigning a subview “springs and struts.” A spring can expand and contract; a strut can’t. Springs and struts can be assigned internally or externally, horizontally or vertically. With two internal springs or struts, you specify whether and how the view can be resized; with four external springs or struts, you specify whether and how the view can be repositioned:
- Imagine a subview that is centered in its superview and is to stay centered, but is to resize itself as the superview is resized. It would have four struts externally and two springs internally.
- Imagine a subview that is centered in its superview and is to stay centered, and is not to resize itself as the superview is resized. It would have four springs externally and two struts internally.
- Imagine an OK button that is to stay in the lower right of its superview. It would have two struts internally, two struts externally from its right and bottom, and two springs externally from its top and left.
- Imagine a text field that is to stay at the top of its superview. It is to widen as the superview widens. It would have three struts externally and a spring from its bottom; internally it would have a vertical strut and a horizontal spring.
In code, a combination of springs and struts is set through a view’s
autoresizingMask property, which is a bitmask (UIView.AutoresizingMask) so that you can combine options. The options represent springs; whatever isn’t specified is a strut. The default is the empty set, apparently meaning all struts — but of course it can’t really be all struts, because if the superview is resized, something needs to change, so in reality an empty
autoresizingMask is the same as
.flexibleRightMargin together with
.flexibleBottomMargin, meaning the view is pinned by struts to the top left.
In debugging, when you log a UIView to the console, its
autoresizingMask is reported using the word
autoresize and a list of the springs. The external springs are
BM; the internal springs are
autoresize = LM+TM means there are external springs from the left and top;
autoresize = W+BM means there’s an internal horizontal spring and a spring from the bottom.
To demonstrate autoresizing, I’ll start with a view and two subviews, one stretched across the top, the other confined to the lower right (Figure 1-13):
let v1 = UIView(frame:CGRect(100, 111, 132, 194)) v1.backgroundColor = UIColor(red: 1, green: 0.4, blue: 1, alpha: 1) let v2 = UIView(frame:CGRect(0, 0, 132, 10)) v2.backgroundColor = UIColor(red: 0.5, green: 1, blue: 0, alpha: 1) let v1b = v1.bounds let v3 = UIView(frame:CGRect(v1b.width-20, v1b.height-20, 20, 20)) v3.backgroundColor = UIColor(red: 1, green: 0, blue: 0, alpha: 1) self.view.addSubview(v1) v1.addSubview(v2) v1.addSubview(v3)
To that example, I’ll add code applying springs and struts to the two subviews to make them behave like the text field and the OK button I was hypothesizing earlier:
v2.autoresizingMask = .flexibleWidth v3.autoresizingMask = [.flexibleTopMargin, .flexibleLeftMargin]
Now I’ll resize the superview, bringing autoresizing into play; as you can see (Figure 1-14), the subviews remain pinned in their correct relative positions:
v1.bounds.size.width += 40 v1.bounds.size.height -= 50
If auto resizing isn’t sophisticated enough to achieve what you want, you have two choices:
- Combine it with manual layout in
layoutSubviews. Autoresizing happens before
layoutSubviewsis called, so your
layoutSubviewscode is free to come marching in and tidy up whatever autoresizing didn’t get quite right.
- Use autolayout. This is actually the same solution, because autolayout is in fact a way of injecting functionality into
layoutSubviews. But using autolayout is a lot easier than writing your own