We'll start building our app by implementing the UI in Interface Builder. For reference, here are the tip calculator designs:

Design Breakdown

Creating Views

We'll get started by creating our header view with a UIView.

UIKit has it's own header-like bar called the UINavigationBar. To keep things simple, we'll start from scratch and create our own header view instead of using iOS's UINavigationBar.

Open Main.storyboard from your project navigator. You should see your single view controller. Starting Storyboard

Next, we'll add a UIView and reposition/resize it to be our header view.

Create a header view by dragging an UIView object from the Object Library to the top of the view controller. Don't worry too much about the perfect size and position for now. We'll handle that later.

The new UIView object that we just added is going to be our custom header view. We'll add other subviews onto it later.

You might notice, that our header view is a little hard to see because it's the same color as the view controller's root view: white. Let's change the color of our root view to add some contrast.

Change the view controller's root view to off-white:

  1. Select the view controller's root view by either clicking on it in your storyboard or selecting it in the document outline. If you don't see it in the Document Outline, you might have to expand the View Controller Scene tree. Make sure you're not selecting the header view by accident. Select Root View
  2. With the root view still selected, open the Attributes Inspector in the Utilities area. Open Attributes Inspector
  3. Next, click on the blue dropdown button beside the active color for the Background Color field. Click Background Color Dropdown
  4. Finally, select the Off-White color under the Named Colors subheader in the dropdown menu. Select Off-White Background Color

We've changed the background color attribute of the root view to a different color. The Off White color we chose was pre-defined in our Assets.xcasset asset catalog.

Before we add more views or configure more properties, let's learn about the iOS coordinate system and the frame attribute of UIView and it's subclasses. We'll need to learn about the iOS coordinate system to properly position and size our views.

iOS Coordinate System

You can think of your phone screen as a coordinate system with it's origin in the top-left corner.

iOS Coordinate System

As we've previously discussed, view can be represented as rectangles drawn on our device's screen. This rectangle can be represented by it's starting point (top-left corner of the rectangle) along with it's size (width and height). Let's look at an example:

Coord Example

In the image above, what is the red view's starting point? What about it's size?

As you can see from the numbered X and Y axis, the view rectangle starts at the point (27, 48) in the iOS coordinate system and has a width of 50pts and a height of 35pts.

In Swift, we have the CGPoint and CGSize data types to represent coordinate points and sizes respectively. A CGPoint value is a pair of X and Y values. A CGSize value is a pair of width and height values.

Additionally, these two data types can be combined into CGRect data type (X, Y, width, height) that represents a rectangle represented by it's point and size properties.

Each UIView has a property called it's frame of type CGRect. You can use each view's frame property to manipulate it's position and size.

At some point, you'll come across another view property named bounds that's also a CGRect. The frame of a view represents the view's rectangle in it's super view's coordinate system while it's bound property refers to the rectangle using the view's top-left corner as the origin of it's coordinate system. In other words, the bounds property of a view will always have an (x, y) of (0, 0) and retain it's size.

Frame vs Bounds

With our new knowledge, let's properly re-position and re-size our header view.

Setting The Header View Rect

Looking back at our design, we can determine the CGRect of our header view.

What should the CGRect (x, y, width, height) of our header view be?

Header View Dimensions

The frame of our header view is (0, 0, 375, 105).

Let's change our current header view's frame in storyboard.

In Main.storyboard perform the following:

  1. Select the header view (UIView) in Interface Builder
  2. With the header view selected, open the Size Inspector in the Utilities area.
  3. Find the view's Frame Rectangle fields. Change the X, Y, Width and Height values in the size inspector to the value of the rect in the solution above.

Fixed Header View

Let's see if our changes worked! Build and run the app in the iPhone 8 simulator by clicking the run button in the toolbar.

You should see the custom header view against your off-white root view in the simulator. Nothing fancy yet!

Fixed Header Simulator

But what happens if we run our app in a simulator with a different size screen?

Handling Different Screen Sizes

Let's revisit our previous diagram explaining a view's frame within the iOS coordinate system.

Header View Dimensions

What would happen if our app was ran across multiple different screen sizes?

Fixed Header View With Different Screen Sizes

As you can see, the frame of the UIView needs to be different for each device with a different screen size.

Can you think of some ways of how we could solve this problem?

Introducing Auto-Layout

One way we could solve different frames for each screen size is by programmatically calculating and setting each view's frame. However, that would be super messy and lead to us having to write a lot of code just make sure each view is the right size for each screen.

To solve this problem, Apple created a relative positioning tool called Auto-Layout. With Auto-Layout, we define constraints.

Constraints are rules where you can define the relative positioning or size between two views. Auto-Layout will then calculate all the math and set our view's frame so that all of the constraints (rules) are followed. This allows us to build dynamic view layouts that re-position and re-shape for any screen size.

For example, we could give our example view the following constraints:

  • Top: 20pts from Super View (Root View) Top Edge
  • Leading (Left): 40pts from Super View (Root View) Leading Edge
  • Trailing (Right): -80pts from Super View (Root View) Trailing Edge
  • Bottom: -380pts from bottom

Note the positive and negative values that are based on the direction of the iOS coordinate system.

Constraints Example

Now if the screen changes, let's see how our view will react:

Constraints Example

See how auto-layout calculates the view's frame based on our constraints for each different screen size?

If instead, we wanted give the view a fixed width or height, we can also add constraints as fixed constants. Let's give our example view a new set of constraints:

  • Top: 20pts from Super View (Root View) Top Edge
  • Leading (Left): 40pts from Super View (Root View) Leading Edge
  • Width: 150pts
  • Height: 200pts

Fixed Size Constraints Example

Auto-layout and constraints give us an easy way to build dynamic view layouts for any iOS device.

Determining Constraints

Let's set our first constraints by changing our header view in Interface Builder to make use of constraints.

What constraints should we set for our header view? Header View Dimensions

Our header view would have the following constraints:

  • Top: 0 from Super View (Root View) Top Edge
  • Leading (Left): 0 from Super View (Root View) Leading Edge
  • Trailing (Right): 0 from Super View (Root View) Trailing Edge
  • Height: 105 fixed constant

Looks pretty good. Let's look at our header view with these constraints across each device:

Header View With Fixed Height Constraint

Hold on. Not so fast. With the introduction of the iPhone X, the sensor housing (the top notch) requires to add some additional thought to our header view frame.

Header View Fixed Height Problem

Because the top notch, we'll need to make calculate the header view's height based on the bottom of the top notch for the iPhone X.

To help us handle this, Apple has provided us with the Safe Area.

Safe Area

The Safe Area provides us with valuable layout information to help us properly create constraints for our views. In our case, the top Safe Area provides us with the bottom of the Status Bar for each device:

Safe Area

Revise our original constraints, we'll need to replace our height constraint with a bottom constraint that is -85 from the top Safe Area. Now our header view dynamically calculate it's layout correctly across each device.

Header View Correct Height

It might be a little hard to see, but the height of the header view is slightly bigger for the iPhone X because of it's top notch.

With our correct constraints, let's set them in Interface Builder.

Setting Our First Constraints

Let's set our constraints in Interface Builder. First we'll start by adding our top, leading (left) and trailing (right) constraints.

Open Main.storyboard from your Project Navigator. Select your header view (UIView) and add the following constraints:

With our header view selected, we click on the Add New Constraints button and set each of the edge constraints:

  • Top Edge of header view 0pts to Top Edge of root view
  • Leading (Left) Edge of header view 0pts to Leading (Left) Edge of root view
  • Trailing (Right) Edge of header view 0pts to Trailing (Right) Edge of root view

Currently, our header view has a incomplete set of constraints. We haven't added a constraint to define the view's height yet. If we run the app now, we won't see our header view because it's height will be 0. Xcode and Interface Builder try to warn us of this:

Constraint Errors

You'll notice above:

  1. a red error arrow that lists missing constraints in your document outline
  2. red highlights around the custom header view in your storyboard
  3. a warning in the Xcode project status bar

Let's add the final constraint to define the header view's height.

Add a constraint from the bottom edge of the header view to the top edge of the Safe Area:

To add the constraint in the video, follow the steps below:

  1. Select the header view (UIView) in the Document Outline.
  2. With the header view selected, hold down the control button (ctrl) and click-drag from the header view to the Safe Area view in your Document Outline.
  3. Once you let go, you'll see a pop-up with the options to add a new constraint. Select Vertical Spacing. This will set a vertical spacing constraint from the top edge of our header view to the top edge of the Safe Area.
  4. (Optional) If you'd like to adjust the constraint, you can click on it and adjust it's values in the Size Inspector.

Congrats, we've added our first set of constraints to our header view. Try running your app in multiple different simulators and see if our header view properly adjusts it's frame to each device.

Auto-Layout Header View Multiple Devices

It works! Next, we'll dive deeper into auto-layout and the different kind of constraints that are available to use.

Different Types of Constraints

To properly setup constraints for our header view, we've only used one type of constraints but there are many different types of constraints that you can use to build complex UI layouts. Before setting up constraints for any of our other fews, let's look at common constraints we can use to build dynamic layouts with auto-layout.

Relative Positioning

First, let's review our relative positioning constraint. Relative positioning allows to position a view relative to another view. For example, we can create a constraint that positions the blue view 45pts from the trailing (right) side of the red view:

Relative Positioning Positive

Positive and negative values (relative to the iOS coordinate system) denote the direction of the constraint. For example, we can instead add a constraint that positions the blue view -75pts from the trailing (right) side of the red view:

Relative Positioning Negative

When you're creating relative positioning constraints, you'll need to keep in mind which view's edge the constraint is starting from, the other view's edge where the constraint is ending at and the position or negative value (direction) of the constraint.

When you're setting your relative positioning constraints, make sure you're aware of what they're relative to. For example, let's set the blue view 20pts below the red view:

Relative Positioning Top Edge

But is that what we wanted? In the case above, we set our blue view to have a constraint of 20pts below the red view but relative to the wrong edge of the red view!

This is probably what we really intended:

Relative Positioning Bottom Edge

As you can see, it's a very common mistake to accidentally set constraints relative to the wrong edge or sometimes even the wrong view!

It's also important to take the Safe Area into consideration when setting a view relative to the root view. If you're setting the top edge of your view to the top edge of the root view, you'll need to verify that you don't accidentally set it to the top edge of the Safe Area instead, or vice versa.

Constant Size (Height or Width)

As we briefly discussed earlier, it's also possible to set fixed constant constraints. These are used to set a fixed width or height of a view. For example, we can give a view a fixed width and height of 100pts:

Fixed Size Constraints

In this case, the red view will always remain the same size (100x100) regardless of changing screen sizes.

If you run into a situation where you've added your constraints but don't see your view, you might have forgotten to add certain constraints. Remember, each view's frame must be able to be determined by it's auto-layout constraints.

In the previous example, if we forgot to add the height constraint, our view wouldn't show up because the height of it's frame is 0.

Center (With Offset) In Superview

Another positioning constraint we can use is aligning center axes vertically or horizontally. For example, we can create a constraint that vertically aligns the blue view's center to red views:

Centers Vertically Aligned

Or we horizontally align the two views:

Centers Horizontally Aligned

You can also choose to offset (positive or negative to determine direction) from the superview:

Centers Vertically Aligned Offset

Aspect Ratio

Aspect ratio constraints are also available. You can set the height to be a ratio of the width or vice versa. This can be useful if you want to make sure the view is always a square (1:1 aspect ratio) or if you decide that the height will always be 1/2 of the width (1:2 aspect ratio.)

In the following example, we set the aspect ratio to (1:3) where the height is a third of the width:

Aspect Ratio

Equal (Ratio) To Other Constraint

The last constraint that we'll cover is the ability to set constraints relative to the ratio of another constraint. This is useful when we want certain views to size themselves relative to other views. For example, we can set the height of the blue view to be (1:2), or half, of the red view:

Relative Rati0

In our tip calculator, we'll use this constraint to size the input and output cards to be of equal heights.

Setting Auto-Layout For Our View Grouping

Let's take an other look at our design:

TC Design

Next, we're going to setup the main views (and their constraints) for each UI group. In other words, we'll add the objects and constraints below:

UI Groups With Dimensions

Implementing Our Constraints

We've already finished implementing the foundation for our header view. We'll repeat a similar process for each of our remaining UI groups.

With our header complete, let's move on to implementing the tip input card.

Input Card View

Open Main.storyboard. Add a new UIView and set the following constraints:

Step by step:

  1. Drag a UIView from the Object Library onto the root view.
  2. Click the Add New Constraints button at the bottom right corner of the Interface Builder Editor window.
  3. Set the following constraints:
    • (Input Card) Top Edge 24pts from Header View Bottom Edge
    • (Input Card) Leading (Left) Edge 15pts from Super View (Root View) Leading (Left) Edge
    • (Input Card) Trailing (Right) Edge 15pts from Super View Trailing (Right) Edge

At this point, you'll see an auto-layout error because your new (input card) view is missing a height constraint. Ignore this warning for now, we'll fix this soon.

Next, we'll add our output card and it's constraints.

Output Card View

In storyboard, add a new UIView and set the following constraints:

Step by step:

  1. Drag a UIView from the Object Library onto the view controller's root view, below the input card.
  2. Click the Add New Constraints button at the bottom right corner of the Interface Builder Editor window.
  3. Set the following constraints:
    • (Output Card) Top Edge 24pts from Input Card Bottom Edge
    • (Output Card) Leading Edge 15pts from Super View Leading Edge
    • (Output Card) Trailing Edge 15pts from Super View Trailing Edge

We'll also add an equal height constraints between both input and output card views.

Add an equal heights constraint between both input and output cards:

Step by step:

  1. Select the output card view.
  2. With the output card selected, hold down shift and then click on the input card view. This will allow you to select both card views.
  3. Click the Add New Constraints button at the bottom right corner of the Interface Builder Editor window.
  4. In the popup prompt, select Equal Heights and add the selected constraint.

Xcode should still show an auto-layout error because we haven't added enough constraints for it determine the height of each card view. Ignore this warning for now, this will be fixed once we add our reset button.

Reset Button

In storyboard, add a new UIButton and set the following constraints:

Step-by-step:

  1. Drag a UIButton from the Object Library onto the view controller's root view, below the output card.
  2. Click the Add New Constraints button at the bottom right corner of the Interface Builder Editor window.
  3. Set the following constraints:
    • (Reset Button) Top Edge 24pts from Output Card Bottom Edge
    • (Reset Button) Leading Edge 15pts from Super View Leading Edge
    • (Reset Button) Trailing Edge 15pts from Super View Trailing Edge
    • (Reset Button) Bottom Edge 24pts from Super View Bottom Edge
    • (Reset Button) Height of 60pts

By default, our button has a clear background color. To make our reset button easier to see, let's change it's background color from Clear to tcDarkBlue.

Change the Background color of the reset button:

Step-by-step:

  1. Select the Reset Button.
  2. With the Reset Button selected, navigate to the Attributes Inspector in the Utilities area.
  3. Scroll down until you find the Background field. This field allows you to set the button's background color.
  4. Locate the blue dropdown button and set the button's background color from Clear to tcDarkBlue.

Our auto-layout warning is gone! After adding our reset button and it's constraints, auto-layout can calculate the height of each input/output card using the equal heights constraint.

We've finished implementing the main view for each of our respective UI groups. For each group, we added the appropriate UIView object and set it's corresponding constraints.

Before moving on, let's test that everything looks as expected.

Testing Our Constraints

To catch bugs or missteps early, it's always good to build and run your code often. Let's go ahead and do that now to test that our constraints are working correctly.

In the toolbar, click the Run button.

If everything goes as expected, you should see the following in your simulator:

Finished UI Groups

Try running our project on different simulators. You'll notice that our view dynamically adjust and re-size for any screen size:

Finished UI Groups Different Devices

Conclusion

In this section, we learned about how to layout our UI; first with frames and later with auto-layout. We learned about constraints and their importance in building dynamic view layouts for multiple devices. And finally, we put our knowledge into practice by implementing a scaffolding for our tip calculator design.

In the next section, we'll build off of our UI by fully implementing and styling each of our UI groups.

Feedback

If you have feedback on this tutorial or find any mistakes, please open issues on the GitHub Repository or comment below.

Summer academy

An iOS Development Summer Course

Design, code and launch your own app. Locations in San Francisco and Asia

Find your location

Product College

A computer science college

Graduate into a successful career as a founder or software engineer.

Learn more
Please note that Make School no longer supports Internet Explorer

We recommend upgrading to a modern web browser. Learn more