DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • Power BI Embedded Analytics — Part 3: Power BI Embedded Demo
  • DGS GraphQL and Spring Boot
  • Auto-Instrumentation in Azure Application Insights With AKS
  • Deploying a Scala Play Application to Heroku: A Step-by-Step Guide

Trending

  • How to Ensure Cross-Time Zone Data Integrity and Consistency in Global Data Pipelines
  • IoT and Cybersecurity: Addressing Data Privacy and Security Challenges
  • Endpoint Security Controls: Designing a Secure Endpoint Architecture, Part 2
  • Cloud Security and Privacy: Best Practices to Mitigate the Risks

Building Responsive iOS Applications Using Markup

A tutorial on using the UIStackView class in iOS 9 to build responsive mobile applications.

By 
Greg Brown user avatar
Greg Brown
·
Jan. 12, 16 · Tutorial
Likes (3)
Comment
Save
Tweet
Share
7.4K Views

Join the DZone community and get the full member experience.

Join For Free

Working with auto layout in iOS has historically been difficult. Initially, the platform provided no support for auto layout at all, requiring developers to either hard-code view sizes and positions or write a lot of complex manual layout code. Layout constraints, introduced in iOS 6, were a step in the right direction, but were still cumbersome to use.

With the introduction of the UIStackView class in iOS 9, it seemed as though Apple finally "got it". Using stack views, it is possible to create complex view hierarchies that automatically adjust to device size and orientation changes without needing to resort to manual size calculations or constraint gymnastics.

Unfortunately, although UIStackView allows developers to implement auto layout-driven, or "responsive", applications more easily than ever before, it is still not quite as convenient as it could be. As with other view types, stack views must be created either interactively using storyboards or programmatically in code. Other platforms, including Android and Windows, allow developers to create user interfaces declaratively using an XML-based markup language that parallels the view hiearchy of the application. Building an interface in markup makes it easy to visualize the resulting output as well as recognize differences between revisions. It is also a metaphor that many developers are comfortable with, thanks to the ubiquity of HTML and the World Wide Web.

MarkupKit is an open-source project that brings this capability to iOS. Using MarkupKit, developers can declare the structure of a user interface rather than using a GUI builder or writing the UI code by hand. For example, the following markup creates an instance of UILabel and sets the value of its text property to "Hello, World!":

<UILabel text="Hello, World!"/>

The output produced by this markup is identical to the output of the following Swift code:

let label = UILabel()
label.text = "Hello, World!"

Simple Stack View Example

Apple's developer documentation includes a collection of tutorials that illustrate how to incorporate auto layout into an iOS application. Two of these examples focus on UIStackView. The first one, called "Simple Stack View", demonstrates the use of a vertical stack view to automatically arrange a label, image view, and button:

Image title

In Apple's example, the developer is instructed to create a storyboard containing the user interface elements using Interface Builder. The same results can be achieved in markup as follows:

<LMLayerView backgroundColor="#ffffff">
    <UIStackView axis="vertical" layoutMarginsRelativeArrangement="true" layoutMarginTop="20" layoutMarginBottom="20" spacing="8">
        <UILabel text="Flowers" font="body" textAlignment="center"/>

        <UIImageView image="flowers" contentMode="scaleAspectFit"
            horizontalContentHuggingPriority="250"
            verticalContentHuggingPriority="249"
            horizontalContentCompressionResistancePriority="750"
            verticalContentCompressionResistancePriority="749"/>

        <UIButton style="systemButton" normalTitle="Edit"/>
    </UIStackView>
</LMLayerView>

The following simple controller code is used to load the markup and provide navigation to the next example:

import UIKit
import MarkupKit

class SimpleStackViewController: UIViewController {
    override func loadView() {
        view = LMViewBuilder.viewWithName("SimpleStackView", owner: self, root: nil)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        title = "Simple Stack View"

        navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: UIBarButtonItemStyle.Plain,
            target: nil, action: nil)
        navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Next", style: UIBarButtonItemStyle.Plain,
            target: self, action: "next")

        edgesForExtendedLayout = UIRectEdge.None
    }

    func next() {
        navigationController!.pushViewController(NestedStackViewController(), animated: true)
    }
}

The markup version uses an instance of LMLayerView as the root element, since this class will automatically create constraints to pin its children to its own bounds. Additionally, the layoutMargins property of the stack view is used to create the vertical gap at the top and bottom of the view instead of creating explicit offset constraints. Otherwise, the example is identical to the Apple version.

Nested Stack Views

Apple's second stack view example, "Nested Stack Views", demonstrates the use of multiple stack views in varying orientations to create a more complex layout:

Image title

Since stack views alone cannot be used to create the layout, several additional constraints are created to maintain a fixed aspect ratio for the image view and to ensure that the form fields are vertically aligned.

The markup for creating the nested stack view example is as follows:

<LMLayerView backgroundColor="#ffffff">
    <!-- Root Stack -->
    <UIStackView axis="vertical" layoutMarginsRelativeArrangement="true" layoutMarginTop="20" layoutMarginBottom="20" spacing="8">
        <!-- Upper Stack -->
        <UIStackView axis="horizontal" spacing="8">
            <!-- Image View -->
            <UIImageView id="imageView" image="square_flowers"
                horizontalContentCompressionResistancePriority="48"
                verticalContentCompressionResistancePriority="48"/>

            <!-- Name Rows Stack -->
            <UIStackView axis="vertical" spacing="8">
                <!-- First Name Stack -->
                <UIStackView axis="horizontal" alignment="firstBaseline" spacing="8">
                    <UILabel text="First" font="body"
                        horizontalContentHuggingPriority="251"
                        verticalContentHuggingPriority="251"/>
                    <UITextField id="firstNameTextField" placeholder="Enter First Name" font="System 14" borderStyle="roundedRect"
                        horizontalContentHuggingPriority="48"
                        horizontalContentCompressionResistancePriority="749"/>
                </UIStackView>

                <!-- Middle Name Stack -->
                <UIStackView axis="horizontal" alignment="firstBaseline" spacing="8">
                    <UILabel text="Middle" font="body"
                        horizontalContentHuggingPriority="251"
                        verticalContentHuggingPriority="251"/>
                    <UITextField id="middleNameTextField" placeholder="Enter Middle Name" font="System 14" borderStyle="roundedRect"
                        horizontalContentHuggingPriority="48"
                        horizontalContentCompressionResistancePriority="749"/>
                </UIStackView>

                <!-- Last Name Stack -->
                <UIStackView axis="horizontal" alignment="firstBaseline" spacing="8">
                    <UILabel text="Last" font="body"
                        horizontalContentHuggingPriority="251"
                        verticalContentHuggingPriority="251"/>
                    <UITextField id="lastNameTextField" placeholder="Enter Last Name" font="System 14" borderStyle="roundedRect"
                        horizontalContentHuggingPriority="48"
                        horizontalContentCompressionResistancePriority="749"/>
                </UIStackView>
            </UIStackView>
        </UIStackView>

        <!-- Text View -->
        <UITextView text="Notes:" font="body" backgroundColor="#aaaaaa"
            verticalContentHuggingPriority="249"/>

        <!-- Button Stack -->
        <UIStackView axis="horizontal" alignment="firstBaseline" distribution="fillEqually" spacing="8">
            <UIButton style="systemButton" normalTitle="Save"/>
            <UIButton style="systemButton" normalTitle="Cancel"/>
            <UIButton style="systemButton" normalTitle="Clear"/>
        </UIStackView>
    </UIStackView>
</LMLayerView>

Again, an LMLayerView is used as the root element and the top/bottom gap is created using the layoutMargins property of the root stack view, but otherwise the content of the markup example is identical to the storyboard version. The additional constraints are created by the view controller when the view is loaded, as shown below:

import UIKit
import MarkupKit

class NestedStackViewController: UIViewController {
    var imageView: UIImageView!

    var firstNameTextField: UITextField!
    var middleNameTextField: UITextField!
    var lastNameTextField: UITextField!

    override func loadView() {
        view = LMViewBuilder.viewWithName("NestedStackView", owner: self, root: nil)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        title = "Nested Stack Views"

        navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: UIBarButtonItemStyle.Plain,
            target: nil, action: nil)
        navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Next", style: UIBarButtonItemStyle.Plain,
            target: self, action: "next")

        edgesForExtendedLayout = UIRectEdge.None

        // Create custom constraints
        NSLayoutConstraint.activateConstraints([
            // Image view aspect ratio
            NSLayoutConstraint(item: imageView, attribute: NSLayoutAttribute.Width,
                relatedBy: NSLayoutRelation.Equal, toItem: imageView, attribute: NSLayoutAttribute.Height,
                multiplier: 1.0, constant: 0),

            // Equal text field widths
            NSLayoutConstraint(item: middleNameTextField, attribute: NSLayoutAttribute.Width,
                relatedBy: NSLayoutRelation.Equal, toItem: firstNameTextField, attribute: NSLayoutAttribute.Width,
                multiplier: 1.0, constant: 0),
            NSLayoutConstraint(item: lastNameTextField, attribute: NSLayoutAttribute.Width,
                relatedBy: NSLayoutRelation.Equal, toItem: middleNameTextField, attribute: NSLayoutAttribute.Width,
                multiplier: 1.0, constant: 0)
        ])
    }

    func next() {
        ...
    }
}

Summary

This article provided an introduction to how iOS application development can be simplified using markup. In addition to auto layout, MarkupKit also provides a number of other features for simplifying app development including CSS-like styling and dynamic localization. For more information, please visit the MarkupKit project on GitHub. Additional code samples can be found in the project wiki.

application

Opinions expressed by DZone contributors are their own.

Related

  • Power BI Embedded Analytics — Part 3: Power BI Embedded Demo
  • DGS GraphQL and Spring Boot
  • Auto-Instrumentation in Azure Application Insights With AKS
  • Deploying a Scala Play Application to Heroku: A Step-by-Step Guide

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!