Chapters

Hide chapters

iOS Apprentice

Eighth Edition · iOS 13 · Swift 5.2 · Xcode 11

My Locations

Section 4: 11 chapters
Show chapters Hide chapters

Store Search

Section 5: 13 chapters
Show chapters Hide chapters

25. The Final App
Written by Eli Ganim

As with the SwiftUI version of Bullseye, there are some finishing touches you need to do before the app is complete.

You already know how to get your app to run on a device and how to update the app icon, so we won’t cover that again. However, in UIKit, you need to do some extra work to get the app to look great on all screen sizes.

You’ve been developing and testing for a 4.7” screen like those found on devices such as the iPhone 8. But what about other iPhones such as the 5.5-inch iPhone Plus or the 5.8-inch iPhone X, which have bigger screens? Or the iPad with its various screen sizes? Will the game work correctly on all these different screen sizes?

This chapter covers the following:

  • Supporting different screen sizes: Ensuring that the app will run correctly on all the different iPhone and iPad screen sizes.
  • Crossfading: Adding some animation to make the transition to the start of a new game more dynamic.

Supporting different screen sizes

First, check if there is, indeed, an issue running Bullseye on a device with a larger screen.

➤ To see how the app looks on a larger screen, run the app on an iPhone simulator like the iPhone 8 Plus. You can switch between simulators using the selector at the top of the Xcode window:

Using the scheme selector to switch to the iPhone 8 simulator
Using the scheme selector to switch to the iPhone 8 simulator

The result might not be what you expected:

On the iPhone 8 simulator, the app doesn’t fill the entire screen
On the iPhone 8 simulator, the app doesn’t fill the entire screen

Obviously, this won’t do. Not everybody is going to be using a 4.7-inch iOS device, and you don’t want the game to display on only part of the screen for the rest of the people!

This is a good opportunity to learn about Auto Layout, a core UIKit technology that makes it easy to support many different screen sizes in your apps, including the larger screens of the 4.7-inch, 5.5-inch and 5.8-inch iPhones as well as the iPad.

Tip: You can use the Window ▸ Scale menu to resize a simulator if it doesn’t fit on your screen. Some of those simulators, like the one for the iPad, can be monsters! Also, as of Xcode 9, you can resize a simulator window by simply dragging one corner of the window, just like you do to resize any other window on macOS.

Interface Builder has a few handy tools to help you make the game fit on any screen.

The background image

➤ Go to Main.storyboard. Open the View as: panel at the bottom and choose the iPhone 8 Plus device. You may need to change the orientation back to landscape.

Viewing the storyboard on iPhone 8 Plus
Viewing the storyboard on iPhone 8 Plus

The storyboard should look like your screen from the iPhone 8 Plus simulator. This shows you how changes on the storyboard affect the bigger iPhone screens.

First, let’s fix the background image. At its normal size, the image is too small to fit on the larger screens.

This is where Auto Layout comes to the rescue.

➤ In the storyboard, select the Background image view on the main view controller and click the small Add New Constraints button at the bottom of the Xcode window:

The Add New Constraints button
The Add New Constraints button

This button lets you define relationships, called constraints, between the currently-selected view and other views in the scene. When you run the app, UIKit evaluates these constraints and calculates the final layout of the views. This probably sounds a bit abstract, but you’ll soon see how it works in practice. In order for the background image to stretch from edge to edge on the screen, the left, top, right and bottom edges of the image should be flush against the screen’s edges. You can use Auto Layout to do this.

➤ In the Add New Constraints menu, set the left, top, right and bottom spacing to zero and make sure that you’ve enabled the red I-beam markers next to (or above/below) each item.

The red I-beams indicate which constraints you’ve enabled when adding new constraints:

Using the Add New Constraints menu to position the background image
Using the Add New Constraints menu to position the background image

➤ Press Add 4 Constraints to finish. The background image will now cover the view fully. Press Undo and Redo a few times to see the difference.

The background image now covers the whole view
The background image now covers the whole view

You might have also noticed that the Document Outline now has a new item called Constraints:

The new Auto Layout constraints appear in the Document Outline
The new Auto Layout constraints appear in the Document Outline

There should be four constraints listed there, one for each edge of the image.

➤ Run the app again on the iPhone 8 Plus Simulator and also on the iPhone 8 Simulator. In both cases, the background should display correctly now. Of course, the other controls are still off-center, but you’ll fix that soon.

If you use the View as panel to switch the storyboard back to the iPhone 8, the background should display correctly there, too.

The About screen

Let’s repeat the background image fix for the About screen, too.

➤ Use the Add New Constraints button to pin the About screen’s background image view to the parent view.

The background image is now fine. Of course, the Close button and web view are still completely off.

➤ In the storyboard, drag the Close button so that it snaps to the center of the view as well as to the bottom guide.

Interface Builder shows a handy guide, the dotted blue line, near the edges of the screen. This is useful for aligning objects by hand.

The dotted blue lines are guides that help position your UI elements
The dotted blue lines are guides that help position your UI elements

You want to create a centering constraint that keeps the Close button in the middle of the screen, regardless of how wide the screen is.

➤ Click the Close button to select it. From the Align menu, which is to the left of the Add New Constraints button, choose Horizontally in Container and click Add 1 Constraint.

The Align menu
The Align menu

Interface Builder now draws a blue bar to represent the constraint. It draws a red box around the button as well.

The Close button has red borders
The Close button has red borders

That’s a problem: The red box indicates that something is wrong with the constraints, which usually means that there aren’t enough of them. The thing to remember is this: For each view, there must always be enough constraints to define both its position and its size.

The Close button already knows its size – you typed it into the Size inspector earlier. But for its position, there’s only a constraint for the X-coordinate (the alignment in the horizontal direction). You also need to add a constraint for the Y-coordinate.

As you’ve noticed, there are different types of constraints — there are alignment constraints and spacing constraints, like the ones you added via the Add New Constraints button. In this case, you want to add a spacing constraint. [TODO: FPE: I added this last sentence, please verify it’s OK –editor]

➤ With the Close button still selected, click on the Add New Constraints button.

You want the Close button to always sit at a distance of 20 points from the bottom of the screen.

➤ In the Add New Constraints menu, in the Spacing to nearest neighbor section, set the bottom spacing to 20 and make sure that you’ve enabled the I-beam above the text box.

The red I-beams determine the sides that you have pinned down
The red I-beams determine the sides that you have pinned down

➤ Click Add 1 Constraint to finish.

The red border will now turn blue, meaning that everything is OK:

The constraints on the Close button are valid
The constraints on the Close button are valid

If you see orange bars instead of blue ones at this point, then something’s still wrong with your Auto Layout constraints:

The views are not positioned according to the constraints
The views are not positioned according to the constraints

This happens when the constraints are valid (otherwise some, or all, of the bars or borders would be red) but the view is not in the right place in the scene. The dashed orange box off to the side is where Auto Layout has calculated the view should be, based on the constraints you have given it.

To fix this issue, select the Close button again and click the Update Frames button at the bottom of the Interface Builder canvas.

The Update Frames button
The Update Frames button

You can also use the Editor ▸ Resolve Auto Layout Issues ▸ Update Frames item from the menu bar.

The Close button should always be perfectly centered now, regardless of the device’s screen size.

Note: What happens if you don’t add any constraints to your views? In that case, Xcode will automatically add constraints when it builds the app. That’s why you didn’t need to bother with any of this before.

However, these default constraints may not always do what you want. For example, they will not automatically resize your views to accommodate larger (or smaller) screens. If you want that to happen, then it’s up to you to add your own constraints. After all, Auto Layout can’t read your mind!

As soon as you add just one constraint to a view, Xcode will no longer add any other automatic constraints to that view. From then on, you’re responsible for adding enough constraints so that UIKit always knows what the position and size of the view will be.

There’s one thing left to fix in the About screen: The web view.

➤ Select the Web View and open the Add New Constraints menu. First, make sure you’ve unchecked Constrain to margins. Click all four I-beam icons so they become solid red and then set their spacing to 20 points, except the bottom one which should be 8 points:

Creating the constraints for the web view
Creating the constraints for the web view

➤ Finish by clicking Add 4 Constraints.

There are now four constraints on the web view, indicated by the blue bars on each side:

The four constraints on the web view
The four constraints on the web view

Three of these constraints pin the Web view to the Main view, so that they always resize together, while one connects the Web view to the Close button. This is enough to determine the size and position of the web view in any scenario.

Fixing the rest of the main scene

Back to the main game scene, which still needs some work.

The game now looks a bit lopsided on bigger screens. You’ll fix that by placing all the labels, buttons and the slider into a new “Container” view. Using Auto Layout, you’ll center that Container view in the screen, regardless of how big the screen is.

➤ Select all the labels, the buttons and the slider. You can hold down Command and click them individually, but an easier method is to go to the Document Outline, click on the first view (for me that’s the “Put the Bullseye as close as you can to:” label) and then hold down Shift and click on the last view.

Selecting the views from the Document Outline
Selecting the views from the Document Outline

You should have selected everything but the background image view.

➤ From Xcode’s menu bar, choose Editor ▸ Embed In ▸ View. This places the selected views inside a new container view:

The views are embedded in a new container view
The views are embedded in a new container view

This new view is completely white. You’ll want to change this eventually, but for now, it makes it easier to add the constraints.

➤ Select the newly-added container view and open the Add New Constraints menu. Check the boxes for Width and Height to make constraints for them and leave the width and height at the values specified by Interface Builder. Click Add 2 Constraints to finish.

Pinning the width and height of the container view
Pinning the width and height of the container view

Interface Builder now draws several bars around the view that represent the Width and Height constraints that you just made… but they’re red. Don’t panic! It only means that there aren’t enough constraints yet. No problem, you’ll add the missing constraints next.

➤ With the container view still selected, open the Align menu. Check the Horizontally in Container and Vertically in Container options, then click Add 2 Constraints.

All of the Auto Layout bars should be blue now, and the view is perfectly centered.

➤ Finally, change the Background color of the container view to Clear Color – in other words, 100% transparent.

You now have a layout that works correctly on any iPhone display! Try it out:

The game running on 4-inch and 5.5-inch iPhones
The game running on 4-inch and 5.5-inch iPhones

Auto Layout may take a while to get used to. Adding constraints in order to position UI elements is a little less obvious than just dragging them into place.

But this also buys you a lot of power and flexibility, which you need when you’re dealing with devices that have different screen sizes.

You’ll learn more about Auto Layout in the other parts of The iOS Apprentice.

Exercise: As you try the game on different devices, you might notice something — the controls for the game are always centered on screen, but they do not take up the whole area of the screen on bigger devices!

This is because you set the container view for the controls to be a specific size. If you want the controls to change position and size depending on how much screen space is available, then you have to remove the container view or set it to resize depending on screen size. You then need to set up the Auto Layout constraints for each control separately.

Are you up to the challenge of doing this on your own?

Compiler warning

There’s a compiler warning about views without any layout constraints that’s been there since from almost the first time you created the main game view.

But where are these views without constraints? The Issue navigator certainly does not give you any clue! You have to go to a different screen to find the list of affected views.

If you look at the Document Outline, you’ll notice that there’s a small yellow circle with an arrow inside it next to the View Controller Scene — an indication that there are some warnings (not errors). If you click this circle, it will bring you to a list of Auto Layout issues in the scene:

List of Auto Layout issues for your scene
List of Auto Layout issues for your scene

You’ll notice that all the controls inside the container view have warnings. This is because none of those views have any Auto Layout constraints, so they’ll remain at their original positions when the view displays.

So how do you fix these warnings? Simple enough — add some constraints for each view so that Auto Layout can determine the view’s size and position when it displays. Of course, you have the necessary knowledge at this point to do that yourself. So I’ll leave it to you to tackle as a personal challenge.

If you get stuck, check out the final project provided for this chapter — that should show you how I fixed the issues.

Testing on iPhone X

So it looks as if you got the app working correctly for all devices, right?

Well… not quite!

Try running the app on the iPhone X simulator. You should see something like this:

The game on iPhone X
The game on iPhone X

Whoa! What happened?

In Xcode 9, Apple introduced a new mechanism to go along with Auto Layout for the iPhone X screen. Since the iPhone X has a notch at the top, you don’t want your app to display its content beneath the notch, since that content would not display properly.

So, Xcode 9 has safe layout guides that define where it’s safe to display content. If you take a look at the Auto Layout constraints for the background image on the main scene (or even the About scene, for that matter), you will notice that the background image aligns to the safe area.

Just to be clear, this is the correct behavior for most apps — you want your content to stick to the safe area. However, in the case of a game like Bullseye where you have a custom background, you want the background to cover the entire screen.

So how do you fix it? Simple enough… just change the image constraints so that they align with the superview, which is the main view for the scene.

➤ Select the background image in the main scene and switch to the Size inspector. It should show the four constraints set on the image:

Auto Layout constraints for your image
Auto Layout constraints for your image

➤ Double-click the first one. You should get a detailed editor for that Auto Layout constraint:

Auto Layout constraint editor
Auto Layout constraint editor

➤ Click the drop-down for First Item and change it from Safe Area to Superview. Also, if the Constant field has a non-zero value, change it to 0.

Edit Auto Layout constraints
Edit Auto Layout constraints

➤ Do the same for the other three constraints. Note that for two of the constraints, Safe Area will be the Second Item and not the First Item. So change either one based on which one specifies Safe Area.

➤ Build and run your app on both the iPhone X simulator and at least one of the other simulators, like the iPhone 8 Plus, to make sure that your changes work correctly on all devices.

Crossfade

There’s one final bit of knowledge I want to impart before calling the game complete — Core Animation. This technology makes it easy to create really sweet animations in your apps with just a few lines of code. Adding subtle animations (with the emphasis on subtle!) can make your app a delight to use.

So next, you’ll add a simple crossfade after the user presses the Start Over, so the transition back to round one won’t seem so abrupt.

➤ In ViewController.swift, change startNewGame() to:

@IBAction func startNewGame() {
  ...
  startNewRound()
  // Add the following lines
  let transition = CATransition()
  transition.type = CATransitionType.fade
  transition.duration = 1
  transition.timingFunction = CAMediaTimingFunction(name: 
                              CAMediaTimingFunctionName.easeOut)
  view.layer.add(transition, forKey: nil)
}

I’m not going to go into too much detail here. Suffice it to say you’re setting up an animation that crossfades from what is currently on the screen to the changes you’re making in startNewRound(). You reset the slider to center position and reset the values of the labels.

➤ Run the app and move the slider so that it’s no longer in the center. Press the Start Over button and you should see a subtle crossfade animation.

The screen crossfades between the old and new states
The screen crossfades between the old and new states

UIKit knowledge unlocked!

You’re now familiar with SwiftUI and UIKit. Well done!

In the Source Code folder for this book, you can find the complete source code for the UIKit version of the Bullseye app. If you’re still unclear about something in this chapter, it’s a good idea to look at this cleaned-up source code.

If you’re feeling exhausted after all that coding, pour yourself a drink and put your feet up for a bit. You’ve earned it! On the other hand, if you just can’t wait to get to grips with more code, go ahead and move on to the next app!

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2026 Kodeco Inc.