In iOS 9, Apple introduced a new means of implementing automatic layout management (or "autolayout") in iOS applications: the UIStackView class. This
UIView subclass arranges its subviews in either a horizontal or vertical line, optionally resizing or redistributing them as needed in response to device size or orientation changes.
In earlier versions of iOS, view layout had to be performed either manually or via layout constraints, neither of which was particularly convenient.
UIStackView significantly simplifies this process. Internally, it uses layout constraints to manage views' size and position, hiding the details of constraint management and absolving developers of the responsibility of working with them directly.
The latest version of MarkupKit adds support for creating and configuring
UIStackView instances in markup. For example, the following document creates a vertical stack view containing three labels:
<UIStackView axis="vertical" alignment="center" spacing="8"> <UILabel text="One"/> <UILabel text="Two"/> <UILabel text="Three"/> <UIView/> </UIStackView>
The labels will be horizontally centered within the stack view and will be separated by 8 pixels of vertical space. The empty trailing
UIView instance is used to create flexible padding space between the final
UILabel and the bottom of the container view, ensuring that the stack view's content is pinned to the top of the view.
This markup produces the following output on an iPhone 6 running iOS 9:
Stack views can be nested to produce more complex layouts. For example, the following markup creates a vertical stack view containing two horizontal stack views. These, in turn, contain three labels and three image views, respectively. Empty views are also used in this example to create flexible padding space:
<UIStackView axis="vertical" spacing="8"> <UIStackView axis="horizontal" spacing="8" layoutMargins="12" layoutMarginsRelativeArrangement="true"> <UILabel text="One"/> <UILabel text="Two"/> <UILabel text="Three"/> <UIView/> </UIStackView> <UIStackView axis="horizontal" spacing="8" layoutMargins="12" layoutMarginsRelativeArrangement="true"> <UIImageView image="vw.png"/> <UIImageView image="mini.png"/> <UIImageView image="chevy.png"/> <UIView/> </UIStackView> <UIView/> </UIStackView>
The "layoutMargins" attribute is used to create a 12-pixel gap around the horizontal stack views' content. Since stack views are configured to ignore layout margins by default, the "layoutMarginsRelativeArrangement" property is set to
true to instruct the stack views to respect the specified margin.
This markup produces the following output:
Even though these examples are somewhat trivial, they offer an effective demonstration of
UIStackView's potential. Many common layout scenarios can be implemented simply by applying various combinations of nested stack views.
Of course, similar layout behavior can be achieved using MarkupKit's existing
LMColumnView classes. Like
UIStackView, these classes use layout constraints internally, hiding the often awkward details of constraint management from developers. The following markup using row and column views produces output identical to the previous stack view-based example:
<LMColumnView> <LMRowView layoutMargins="12"> <UILabel text="One"/> <UILabel text="Two"/> <UILabel text="Three"/> <LMSpacer/> </LMRowView> <LMRowView layoutMargins="12"> <UIImageView image="vw.png"/> <UIImageView image="mini.png"/> <UIImageView image="chevy.png"/> <LMSpacer/> </LMRowView> <LMSpacer/> </LMColumnView>
Note that, since
LMColumnView are configured to respect layout margins by default, it is not necessary to set the "layoutMarginsRelativeArrangement" property. Row and column views also have a default spacing value of 8 pixels, so explicitly setting the "spacing" property is also unnecessary.
Also note that, in this example,
LMSpacer instances are used to create the flexible space between the final views and the edge of the parent container.
LMSpacer is an extremely simple
UIView subclass whose sole purpose is to provide such spacing. It overrides
hitTest:withEvent: method to return
nil to ensure that it does not prevent touch events from reaching underlying views.
LMSpacer can also be used with
MarkupKit's layout views also offer some additional features not provided by
UIStackView. For example, since it is a "non-rendering subclass" of
UIStackView does not allow a caller to set a background color. However, background colors are supported by
LMColumnView. This is an important consideration when creating custom view controllers. Because a
UIStackView cannot have a background color, pushing a stack view-based controller onto a
UINavigationController can cause some rendering issues:
A row or column view-based controller with a background color animates smoothly:
LMColumnView provide an option to distribute subviews by weight, something that does not currently seem to be possible with
UIStackView. Weight-based distribution allows a caller to specify exactly what percentage of the available space within a container should be allocated to a particular subview.
For example, the following markup creates a column view containing two row views. The first row view is assigned a weight of 1 and the second a weight of 2, totalling 3. As a result, the first row will be given 1/3, or about 33% of the available space in the column, and the second row will be given 2/3, or about 66%, of the available space:
<LMColumnView> <LMRowView weight="1" backgroundColor="#ffcccc"> <LMSpacer/> <UILabel text="One"/> <UILabel text="Two"/> <UILabel text="Three"/> <LMSpacer/> </LMRowView> <LMRowView weight="2" backgroundColor="#ccffcc"> <LMSpacer/> <UILabel text="Four"/> <UILabel text="Five"/> <UILabel text="Six"/> <LMSpacer/> </LMRowView> </LMColumnView>
The output of this markup when run in portait mode looks like this:
In landscape, it looks like this:
UIStackView requires a device to be running iOS 9 or later.
UIColumnView are supported on iOS 8 and above.
On the other hand,
UIStackView provides some capabilities that are not supported by
LMColumnView. For example, it offers several additional distribution options as well as the ability to animate changes to its content and properties. See the
UIStackView class documentation for more information on these features.
So, when should you use one or the other? Both view types can be used in the same application as well as within the same view hierarchy. If you need the ability to specify a background color on your container, want to distribute subviews by weight, or need to support iOS 8, use a row or column view. If you need to animate layout changes or want to distribute subviews by other means and you are targeting iOS 9 exclusively, use a stack view.
By combining the capabilities of
UIStackView with those of
LMColumnView, you can quickly and easily create complex user interfaces that will automatically adapt to size and orientation changes, providing a much more responsive and engaging interface for your users.