Chapters

Hide chapters

UIKit Apprentice

Second Edition · iOS 15 · Swift 5.5 · Xcode 13

My Locations

Section 3: 11 chapters
Show chapters Hide chapters

Store Search

Section 4: 13 chapters
Show chapters Hide chapters

7. The New Look
Written by Fahim Farook

Bull’s Eye is looking good, the gameplay elements are done, and there’s one item left in your to-do list — “Make it look pretty”.

You have to admit the game still doesn’t look great. If you were to put this on the App Store in its current form, I’m not sure many people would be excited to download it. Fortunately, iOS makes it easy for you to create good-looking apps, so let’s give Bull’s Eye a makeover and add some visual flair.

This chapter covers the following:

  • Landscape orientation revisited: Project changes to make landscape orientation support work better.
  • Spice up the graphics: Replace the app UI with custom graphics to give it a more polished look.
  • The about Screen: Add an about screen to the app and make it look spiffy.

Landscape orientation revisited

First, let’s quickly revisit another item in the to-do list — “Put the app in landscape orientation.” You already did this, right? But there’s a little bit of clean up to be done with regards to that item.

Apps in landscape mode do not display the iPhone status bar, unless you tell them to. That’s great for your app — games require a more immersive experience and the status bar detracts from that.

Even though the system automatically handles hiding the status bar for your game, there is still one thing you can do to improve the way Bull’s Eye handles the status bar.

➤ Go to the Project Settings screen and scroll down to Deployment Info. Under Status Bar Style, check Hide status bar.

This will ensure that the status bar is hidden during application launch.

Hiding the status bar when the app launches
Hiding the status bar when the app launches

It’s a good idea to hide the status bar while the app is launching. It takes a few seconds for the operating system to load the app into memory and start it up, and during that time the status bar remains visible, unless you hide it using this option.

It’s only a small detail, but the difference between a mediocre app and a great app is that great apps get all the small details right.

➤ That’s it. Run the app and you’ll see that the status bar is history.

Info.plist and Info Tab

Before Xcode 13, most of the options from the Project Settings screen, such as the supported device orientations and whether the status bar is visible during launch, used to be stored in your app’s Info.plist file.

However, in Xcode 13, the Info.plist file, if it exists at all, will be pretty empty. Let’s take a look.

➤ Go to the Project navigator and select the file named Info.plist to take a peek at its contents.

Most of the project settings that used to be stored in Info.plist are now stored in the Xcode project file itself. Since you can make changes to the settings via the Xcode interface (as you did above for the status bar style), not having the settings in Info.plist makes sense.

But if you still want to see all these settings in one place as you used to be able to via Info.plist, you still can do that. You just have to go to the project’s Info tab.

➤ Go to the Project Settings screen and select the Info tab.

The Xcode Info tab
The Xcode Info tab

You should now see a list of configuration options and their values just as you used to do formally via the Info.plist file. Most of these may not make sense to you, but that’s OK – they don’t always make sense to me either.

Notice the option Status bar is initially hidden. It has the value YES. This is the option that you just changed.

Spice up the graphics

Getting rid of the status bar is only the first step. We want to go from this:

Yawn…
Yawn…

To something that’s more like this:

Cool :-]
Cool :-]

The actual controls won’t change. You’ll simply be using images to smarten up their look, and you will also adjust the colors and typefaces. You can put an image in the background, on the buttons, and even on the slider, to customize the appearance of each. The images you use should generally be in PNG format, though JPG files would work too.

Add the image assets

If you are artistically challenged, then don’t worry, I have provided a set of images for you. But if you do have mad Photoshop skillz, then by all means feel free to design (and use) your own images. The Resources folder that comes with this book contains a subfolder named Images. You will first import these images into the Xcode project.

➤ In the Project navigator, find Assets.xcassets and click on it.

This item is known as the asset catalog for the app and it contains all the app’s images. Right now, it is empty and contains just a couple of placeholders.

The asset catalog is initially empty
The asset catalog is initially empty

➤ At the bottom of the secondary pane, the one with AppIcon, there is a + button. Click it and then select the Import… option:

Choose Import to put existing images into the asset catalog
Choose Import to put existing images into the asset catalog

Xcode shows a file picker. Select the Images folder from the resources and press ⌘+A to select all the files inside this folder.

Choosing the images to import
Choosing the images to import

Click Open and Xcode copies all the image files from that folder into the asset catalog:

The images are now inside the asset catalog
The images are now inside the asset catalog

If Xcode added a folder named “Images” instead of the individual image files, then try again and this time make sure that you select the files inside the Images folder rather than the folder itself before you click Open.

Note: Instead of using the Import… menu option as above, you could also simply drag the necessary files from Finder on to the Xcode asset catalog view. As ever, there’s more than one way to do the same thing in Xcode.

1x, 2x, and 3x displays

Each image set in the asset catalog has a slot for a “2x” image, but you can also specify 1x and 3x images. Having multiple versions of the same image in varying sizes allows your apps to support the wide variety of iPhone and iPad displays in existence.

1x is for low-resolution screens, the ones with the big, chunky pixels. There are no low-resolution devices in existence that can actually run iOS 15 – they are too old to bother with – so you’re not likely to come across many 1x images anymore. 1x is only a concern if you’re working on an app that still needs to support iOS 9 or older.

2x is for high-resolution Retina screens. This covers most modern iPhones, iPod touches, and iPads. Retina images are twice as big as the low-res images, hence the 2x. The images you imported just now are 2x images.

3x is for the super high-resolution Retina HD screen of the iPhone Plus devices. If you want your app to have extra sharp images on these top-of-the-line iPhone models, then you can drop them into the “3x” slot in the asset catalog.

There is a special naming convention for image files. If the filename ends in @2x or @3x then that’s considered the Retina or Retina HD version. Low-resolution 1x images have no special name (you don’t have to write @1x).

Put up the wallpaper

Let’s begin by changing the drab white background in Bull’s Eye to something more fancy.

➤ Open Main.storyboard, open the Library panel (via the top toolbar) and locate an Image View.

Tip: if you type “image” into the search box at the top of the Library panel, it will quickly filter out all the other views.

The Image View control in the Objects Library
The Image View control in the Objects Library

➤ Drag the image view on top of the existing user interface. It doesn’t really matter where you put it, as long as it’s inside the Bull’s Eye View Controller.

Dragging the Image View into the view controller
Dragging the Image View into the view controller

➤ With the image view still selected, go to the Size inspector (that’s the one next to the Attributes inspector) and set X and Y to 0, Width to 667 and Height to 375.

This will make the image view cover the entire screen.

The Size inspector settings for the Image View
The Size inspector settings for the Image View

➤ Go to the Attributes inspector for the image view. At the top there is an option named Image. Click the downward arrow and choose Background from the list.

This will put the image named “Background” from the asset catalog into the image view.

Setting the background image on the Image View
Setting the background image on the Image View

There is only one problem: the image now covers all the other controls. There is an easy fix for that; you have to move the image view behind the other views.

➤ With the image view selected, in the Editor menu in Xcode’s menu bar at the top of the screen, choose Arrange ▸ Send to Back.

Sometimes Xcode gives you a hard time with this (it still has a few bugs) and you might not see the Send to Back item enabled. If so, try de-selecting the Image View and then selecting it again. Now the menu item should be available.

Alternatively, select the image view in the Document Outline and drag it to the top of the list of views, just below Safe Area, to accomplish the same thing. (The items in the Document Outline view are listed so that the backmost item is at the top of the list and the frontmost one is at the bottom.)

Your interface should now look something like this:

The game with the new background image
The game with the new background image

That takes care of the background. Run the app and marvel at the new graphics.

Change the labels

Because the background image is quite dark, the black text labels have become hard to read. Fortunately, Interface Builder lets you change label color. While you’re at it, you might change the font as well.

➤ Still in the storyboard, select the label at the top, open the Attributes inspector and click on the Color item to show a dropdown for color values. Select Custom… at the bottom of the list.

Setting the text color on the label
Setting the text color on the label

This opens the Color Picker, which has several ways to select colors. I prefer the sliders (second tab). If all you see is a gray scale slider, then select RGB Sliders from the picker at the top.

The Color Picker
The Color Picker

➤ Pick a pure white color, Red: 255, Green: 255, Blue: 255, Opacity: 100%. Alternatively, you can simply pick White Color from the initial dropdown instead of opening the Color Picker at all, but it’s good to know that the Color Picker is there in case you want to do custom colors.

➤ Click on the Shadow item from the Attributes inspector. This lets you add a subtle shadow to the label. By default this color is transparent (also known as “Clear Color”) so you won’t see the shadow. Using the Color Picker, choose a pure black color that is half transparent, Red: 0, Green: 0, Blue: 0, Opacity: 50%.

Note: Sometimes when you change the Color or Shadow attributes, the background color of the view also changes. This is a bug in Xcode. Put it back to Clear Color if that happens.

➤ Change the Shadow Offset to Width: 0, Height: 1. This puts the shadow below the label.

The shadow you’ve chosen is very subtle. If you’re not sure that it’s actually visible, then toggle the height offset between 1 and 0 a few times. Look closely and you should be able to see the difference. As I said, it’s very subtle.

➤ Click on the [T] icon of the Font attribute. This opens the Font Picker.

By default, the System font is selected. That uses whatever is the standard system font for the user’s device. The system font is nice enough but we want something more exciting for this game.

Font picker with the System font
Font picker with the System font

➤ Choose Font: Custom. That enables the Family field. Choose Family: Arial Rounded MT Bold. Set the Size to 16.

Setting the label’s font
Setting the label’s font

➤ The label also has an attribute Autoshrink. Make sure this is set to Fixed Font Size.

If enabled, Autoshrink will dynamically change the size of the font if the text is larger than will fit into the label. That is useful in certain apps, but not in this one. Instead, you’ll change the size of the label to fit the text rather than the other way around.

➤ With the label selected, press ⌘= on your keyboard, or choose Size to Fit Content from the Editor menu.

(If the Size to Fit Content menu item is disabled, then de-select the label and select it again. Sometimes Xcode gets confused about what is selected. Poor thing.)

The label will now become slightly larger or smaller so that it fits snugly around the text. If the text got cut off when you changed the font, now all the text will show again.

You don’t have to set these properties for the other labels one by one; that would be a big chore. You can speed up the process by selecting multiple labels and then applying these changes to that entire selection.

➤ Click on the Score: label to select it. Hold and click on the Round: label. Now both labels will be selected. Repeat what you did above for these labels:

  • Set Color to pure white, 100% opaque.
  • Set Shadow to pure black, 50% opaque.
  • Set Shadow Offset to width 0, height 1.
  • Set Font to Arial Rounded MT Bold, size 16.
  • Make sure Autoshrink is set to Fixed Font Size.

As you can see, in my storyboard the text no longer fits into the Score and Round labels:

The font is too large to fit all the text in the Score and Round labels
The font is too large to fit all the text in the Score and Round labels

You can either make the labels larger by dragging their handles to resize them manually, or you can use the Size to Fit Content option (⌘=). I prefer the latter because it’s less work.

Tip: Xcode is smart enough to remember the colors you have used recently. Instead of going into the Color Picker all the time, you can simply choose a color from the Recently Used Colors menu which is part of the dropdown you get when you click on any color option:

Quick access to recently used colors and several handy presets
Quick access to recently used colors and several handy presets

Exercise: You still have a few labels to go. Repeat what you just did for the other labels. They should all become white, have the same shadow and have the same font. However, the two labels on either side of the slider (1 and 100) will have font size 14, while the other labels (the ones that will hold the target value, the score and the round number) will have font size 20 so they stand out more.

Because you’ve changed the sizes of some of the labels, your carefully constructed layout may have been messed up a bit. You may want to clean it up a little.

At this point, the game screen should look something like this:

What the storyboard looks like after styling the labels
What the storyboard looks like after styling the labels

All right, it’s starting to look like something now. By the way, feel free to experiment with the fonts and colors. If you want to make it look completely different, then go right ahead. It’s your app!

The buttons

Changing the look of the buttons works very much the same way.

➤ Select the Hit Me! button. In the Size inspector set its Width to 100 and its Height to 37.

➤ Center the position of the button on the inner circle of the background image.

➤ Go to the Attributes inspector. Change Type from System to Custom.

A “system” button just has a label and no border. By making it a custom button, you can style it any way you wish.

➤ Change Style to Default. The Library has multiple types of Button — Button, Gray Button, Tinted Button, and Filled Button — but if you’d selected the plain Button, then your Style would have been set to Plain.

A plain button does not show different states when you tap it. You change the Style to Default to indicate that you want a button with different states for highlighted, selected, disabled etc.

Note that you now have a State Config dropdown which is set to Default. We’ll come back to that in a bit.

➤ Still in the Attributes inspector, press the arrow on the Background field and choose Button-Normal from the list.

➤ Set the Font to Arial Rounded MT Bold, size 20.

➤ Set the Text Color to red: 96, green: 30, blue: 0, opacity: 100%. This is a dark brown color.

➤ Set the Shadow Color to pure white, 50% opacity. The shadow offset should be Width 0, Height 1.

Blending in

Setting the opacity to anything less than 100% will make the color slightly transparent (with opacity of 0% being fully transparent). Partial transparency makes the color blend in with the background and makes it appear softer.

Try setting the shadow color to 100% opaque pure white and notice the difference.

This finishes the setup for the Hit Me! button in its “Default” state:

The attributes for the Hit Me button in the default state
The attributes for the Hit Me button in the default state

As I mentioned above, Default Style buttons can have more than one state. When you tap a button and hold it down, it should appear “pressed down” to let you know that the button will be activated when you lift your finger. This is known as the highlighted state and is an important visual cue to the user.

➤ With the button still selected, click the State Config setting and pick Highlighted from the menu. Now the attributes in this section reflect the highlighted state of the button.

➤ In the Background field, select Button-Highlighted.

➤ Make sure the highlighted Text Color is the same color as before (red 96, green 30, blue 0, or simply pick it from the Recently Used Colors menu). Change the Shadow Color to half-transparent white again.

➤ Check the Reverses On Highlight option. This will give the appearance of the label being pressed down when the user taps the button.

You could change the other properties too, but don’t get too carried away. The highlight effect should not be too jarring.

The attributes for the highlighted Hit Me button
The attributes for the highlighted Hit Me button

To test the highlighted look of the button in Interface Builder you can toggle the Highlighted box in the Control section, but make sure to turn it off again or the button will initially appear highlighted when the screen is shown.

That’s it for the Hit Me! button. Styling the Start Over button is very similar, except you will replace its title text with an icon.

➤ Select the Start Over button and change the following attributes:

  • Set Type to Custom.
  • Set Style to Default.
  • Remove the text “Start Over” from the button.
  • For Image choose StartOverIcon
  • For Background choose SmallButton
  • Check the Size Inspector to ensure that Width and Height are set to 32. This should be done automatically when you set the background image, but it’s good to verify.

You won’t set a highlighted state on this button — let UIKit take care of this. If you don’t specify a different image for the highlighted state, UIKit will automatically darken the button to indicate that it is pressed.

➤ Make the same changes to the button, but this time choose InfoButton for the image.

The user interface is almost done. Only the slider is left…

Almost done!
Almost done!

The slider

Unfortunately, you can only customize the slider a little bit in Interface Builder. For the more advanced customization that this game needs – putting your own images on the thumb and the track – you have to resort to writing code.

Do note that everything you’ve done so far in Interface Builder you could also have done in code. Setting the color on a button, for example, can be done by sending the setTitleColor() message to the button. (You would normally do this in viewDidLoad.)

However, I find that doing visual design work is much easier and quicker in a visual editor such as Interface Builder than writing the equivalent source code. But for the slider you have no choice.

➤ Go to ViewController.swift, and add the following to viewDidLoad():

let thumbImageNormal = UIImage(named: "SliderThumb-Normal")!
slider.setThumbImage(thumbImageNormal, for: .normal)

let thumbImageHighlighted = UIImage(
  named: "SliderThumb-Highlighted")!
slider.setThumbImage(thumbImageHighlighted, for: .highlighted)

let insets = UIEdgeInsets(
  top: 0, 
  left: 14, 
  bottom: 0, 
  right: 14)

let trackLeftImage = UIImage(named: "SliderTrackLeft")!
let trackLeftResizable = trackLeftImage.resizableImage(
  withCapInsets: insets)
slider.setMinimumTrackImage(trackLeftResizable, for: .normal)

let trackRightImage = UIImage(named: "SliderTrackRight")!
let trackRightResizable = trackRightImage.resizableImage(
  withCapInsets: insets)
slider.setMaximumTrackImage(trackRightResizable, for: .normal)

This sets four images on the slider: two for the thumb and two for the track. (And if you’re wondering what the “thumb” is, that’s the little circle in the center of the slider, the one that you drag around to set the slider value.)

The thumb works like a button so it gets an image for the normal (un-pressed) state and one for the highlighted state.

The slider uses different images for the track on the left of the thumb (green) and the track to the right of the thumb (gray).

➤ Run the app. You have to admit it looks fantastic now!

The game with the customized slider graphics
The game with the customized slider graphics

To .png or not to .png

If you recall, the images that you imported into the asset catalog had filenames like SliderThumb-Normal@2x.png and so on.

When you create a UIImage object, you don’t use the original filename but the name that is listed in the asset catalog, SliderThumb-Normal.

That means you can leave off the @2x bit and the .png file extension.

The About screen

Your game looks awesome and your to-do list is done. So, does this mean that you are done with Bull’s Eye?

Not so fast! :] Remember the button on the game screen? Try tapping it. Does it do anything? No?

Oops! Looks as if we forgot to add any functionality to that button! It’s time to rectify that — let’s add an “about” screen to the game which shows some information about the game and have it display when the user taps on the button.

Initially, the screen will look something like this (but we’ll prettify it soon enough):

The new About screen
The new About screen

This new screen contains a text view with the gameplay rules and a button to close the screen.

Most apps have more than one screen, even very simple games. So, this is as good a time as any to learn how to add additional screens to your apps.

I have pointed it out a few times already: each screen in your app will have its own view controller. If you think “screen”, think “view controller”.

Xcode automatically created the main ViewController object for you, but you’ll have to create the view controller for the About screen yourself.

Add a new view controller

➤ Go to Xcode’s File menu and choose New ▸ File… In the window that pops up, choose the Cocoa Touch Class template (if you don’t see it, then make sure iOS is selected at the top).

Choosing the file template for Cocoa Touch Class
Choosing the file template for Cocoa Touch Class

Click Next. Xcode gives you some options to fill out:

The options for the new file
The options for the new file

Choose the following:

  • Class: AboutViewController
  • Subclass of: UIViewController
  • Also create XIB file: Leave this box unchecked.
  • Language: Swift

Click Next. Xcode will ask you where to save this new view controller.

Saving the new file
Saving the new file

➤ Choose the BullsEye folder (this folder should already be selected).

Also make sure Group says BullsEye and that there is a checkmark in front of BullsEye in the list of Targets. (If you don’t see this panel, click the Options button at the bottom of the dialog.)

➤ Click Create.

Xcode will create a new file and add it to your project. As you might have guessed, the new file is AboutViewController.swift.

Design the view controller in Interface Builder

To design this new view controller, you need to pay a visit to Interface Builder.

➤ Open Main.storyboard. There is no scene representing the About view controller in the storyboard yet. So, you’ll have to add this first.

➤ From the Library, choose View Controller and drag it on to the canvas, to the right of the main View Controller.

Dragging a new View Controller from the Objects Library
Dragging a new View Controller from the Objects Library

This new view controller is totally blank. You may need to rearrange the storyboard so that the two view controllers don’t overlap. Interface Builder isn’t very tidy about where it puts things.

➤ Drag a new Button on to the screen and give it the title Close. Put it somewhere around the bottom center of the view (use the blue guidelines to help with positioning).

➤ Drag a Text View on to the view and make it cover most of the space above the button. Note that there is also a Text Field, which is a single-line text component — that’s not what you want. You’re looking for Text View, which can contain multiple lines of text.

After dragging both the text view and the button on to the canvas, it should look something like this:

The About screen in the storyboard
The About screen in the storyboard

➤ Double-click the text view to make its content is editable. By default, the Text View contains a bunch of Latin placeholder text (also known as “Lorem Ipsum”).

Enter this new text into the Text View:

*** Bull’s Eye ***

Welcome to the awesome game of Bull’s Eye where you can win points and fame by dragging a slider.

Your goal is to place the slider as close as possible to the target value. The closer you are, the more points you score. Enjoy!

You can also enter that text into the Attributes inspector’s Text property for the text view if you find that easier.

➤ Make sure to uncheck the Editable checkbox in the Attribute Inspector. Otherwise, the user can actually type into the text view and you don’t want that.

The Attributes inspector for the text view
The Attributes inspector for the text view

That’s the design of the screen done for now.

Show the new view controller

Now that you have an About screen, how do you open this new screen when the user presses the button? Storyboards have a neat trick for this: segues — pronounced “seg-way” like the silly scooters. A segue is a transition from one screen to another. They are really easy to add.

➤ Click the button in the View Controller to select it. Then hold down Control and drag over to the About screen.

Control-drag from one view controller to another to make a segue
Control-drag from one view controller to another to make a segue

➤ Let go of the mouse button and a popup appears with several options. Choose Present Modally.

Choosing the type of segue to create
Choosing the type of segue to create

Now an arrow will appear between the two screens. This arrow represents the segue from the main scene to the About scene.

➤ Click the arrow to select it. Segues also have attributes. In the Attributes inspector, choose Transition, Flip Horizontal. That is the animation that UIKit will use to move between these screens.

Changing the attributes for the segue
Changing the attributes for the segue

➤ Now you can run the app. Press the button to see the new screen.

The About screen appears with a flip animation
The About screen appears with a flip animation

The About screen should appear with a neat animation. Good, that seems to work.

Dismiss the About view controller

Did you notice that there’s an obvious issue here? Tapping the Close button seems to have no effect. Once the user enters the About screen they can never leave… that doesn’t sound like good user interface design, does it?

The problem with segues is that they only go one way. To close this screen, you have to hook up some code to the Close button. As a budding iOS developer you already know how to do that: use an action method!

This time you will add the action method to AboutViewController instead of ViewController, because the Close button is part of the About screen, not the main game screen.

➤ Open AboutViewController.swift and replace its contents with the following:

import UIKit

class AboutViewController: UIViewController {
  @IBAction func close() {
    dismiss(animated: true, completion: nil)
  }
}

The code in the close() action method tells UIKit to close the About screen with an animation.

If you had said dismiss(animated: false, …), then there would be no page flip and the main screen would instantly reappear. From a user experience perspective, it’s often better to show transitions from one screen to another via an animation.

That leaves you with one final step, hooking up the Close button’s Touch Up Inside event to this new close action.

➤ Open the storyboard and Control-drag from the Close button to the About scene’s View Controller. Hmm, strange, the close action should be listed in this popup, but it isn’t. Instead, this is the same popup you saw when you made the segue:

The “close” action is not listed in the popup
The “close” action is not listed in the popup

Exercise: Bonus points if you can spot the error. It’s a very common – and frustrating! – mistake.

The problem is that this scene in the storyboard doesn’t know yet that it is supposed to represent the AboutViewController.

Set the class for a view controller

You first added the AboutViewController.swift source file, and then dragged a new view controller on to the storyboard. But, you haven’t told the storyboard that the design for this new view controller belongs to AboutViewController — that’s why in the Document Outline it just says View Controller and not About View Controller.

➤ Fortunately, this is easily remedied. In Interface Builder, select the About scene’s View Controller and go to the Identity inspector (that’s the tab/icon to the left of the Attributes inspector).

➤ Under Custom Class, for Class enter AboutViewController.

The Identity inspector for the About screen
The Identity inspector for the About screen

Xcode should auto-complete this for you once you type the first few characters. If it doesn’t, then double-check that you really have selected the View Controller and not one of the views inside it. (The view controller should also have a blue border on the storyboard to indicate it is selected.)

Now you should be able to connect the Close button to the action method.

➤ Control-drag from the Close button to About View Controller in the Document Outline (or to the yellow circle at the top of the scene in the storyboard). This should be old hat by now. The popup menu now does have an option for the close action (under Sent Events). Connect the button to that action.

➤ Run the app again. You should now be able to return from the About screen.

OK, that does get us a working about screen, but it does look a little plain doesn’t it? What if you added some of the design changes you made to the main screen?

Exercise: Add a background image to the About screen. Also, change the Close button on the About screen to look like the Hit Me! button and play around with the Text View properties in the Attribute Inspector. You should be able to do this by yourself now. Piece of cake! Refer back to the instructions for the main screen if you get stuck.

When you are done, you should have an About screen which looks something like this:

The new and improved About screen
The new and improved About screen

That looks good, but it could be better :] So how do you improve upon it?

Use a web view for HTML content

➤ Now select the text view and press the Delete key on your keyboard. (Yep, you’re throwing it away, and after all those changes, too! But don’t grieve for the Text View too much, you’ll replace it with something better.)

➤ Put a WebKit View in its place — as always, you can find this view in the Objects Library. There are two web view options — an older Web View, which is deprecated, or ready to be retired, and the WebKit View. Make sure that you select the WebKit View.

This view can show web pages. All you have to do is give it the URL to a web site or the name of a file to load. The WebKit View object is named WKWebView.

For this app, you will make it display a static HTML page from the application bundle, so it won’t actually have to go online and download anything.

➤ Go to the Project navigator and right-click on the BullsEye group (the folder, not the blue icon for the root of the project). From the menu, choose Add Files to “BullsEye”…

Using the right-click menu to add existing files to the project
Using the right-click menu to add existing files to the project

➤ In the file picker, select the BullsEye.html file from the Resources folder. This is an HTML5 document that contains the gameplay instructions.

Choosing the file to add
Choosing the file to add

Make sure that Copy items if needed is selected and that under Add to targets, there is a checkmark in front of BullsEye. (If you don’t see these options, click the Options button at the bottom of the dialog.)

➤ Press Add to add the HTML file to the project.

➤ In AboutViewController.swift, add an outlet for the web view:

class AboutViewController: UIViewController {
  @IBOutlet var webView: WKWebView!
  . . .
}

Xcode will complain soon after you add the above line. The error should look something like this:

Xcode complains about WKWebView
Xcode complains about WKWebView

What does this error mean? It means that Xcode, or rather the compiler, does not know what WKWebView is.

But how can that be? We selected the component from Xcode’s own Objects Library and so it should be supported, right? The answer to this lies with this line of code at the top of both your view controller source files:

import UIKit

I’m sure you saw this line and wondered what it was about. That statement tells the compiler that you want to use the objects from the UIKit framework. Frameworks, or libraries if you prefer, bundle together one or more objects which perform a particular type of task (or tasks). The UIKit library provides all the UI components for iOS.

So why does UIKit not contain WKWebView, you ask? That’s because the previously mentioned deprecated WebView is the one which is included with UIKit. The newer (and improved) WKWebView comes from a different framework called WebKit.

➤ Add the following line at the top of AboutViewController.swift, right below the existing import statement:

import WebKit

That tells the compiler that we want to use objects from the WebKit framework and since now the compiler knows about all the objects in the WebKit framework, the Xcode error will go away.

➤ In the storyboard file, connect the WKWebView to this new outlet. The easiest way to do this is to Control-drag from About View Controller (in the Document Outline) to the Web View.

➤ In AboutViewController.swift, add a viewDidLoad() implementation:

override func viewDidLoad() {
  super.viewDidLoad()

  if let url = Bundle.main.url(
    forResource: "BullsEye", withExtension: "html") {
    let request = URLRequest(url: url)
    webView.load(request)
  }
}

This displays the HTML file using the web view.

The code first gets the URL (Uniform Resource Locator) for the BullsEye.html file in the application bundle. A URL, as you might be familiar with from the Interwebs, is a way to identify the location of a resource, like a web page. Here, the URL provides the location of the HTML file in your application bundle.

It then creates a URLRequest using that URL since that’s one of the easiest ways to send a load request to the web view. Finally, the code asks the web view to load the contents specified by the URL request.

➤ Run the app and press the info button. The About screen should appear with a description of the gameplay rules, this time in the form of an HTML document:

The About screen in all its glory
The About screen in all its glory

Minimap

With two screens on your Interface Builder canvas, you might now begin to see how the minimap on the top right-hand corner of Interface Builder works.

The minimap
The minimap

First of all, the minimap does not have to remain on the top right-hand corner. You can drag it around and place it anywhere you want — the trick is to drag it when the cursor is over the minimap and it is an arrow icon. You will not be able to drag it when the cursor is over the minimap and it is a hand icon. Try it.

When the cursor is a hand icon, you can move the viewport — or the portion of the canvas that is visible to you — within the minimap area so as to focus on a particular part of the canvas or to quickly navigate to the scene that you are looking for.

You can also focus on a particular scene by simply double-clicking it on the minimap.

When you have a fairly big canvas with lots of scenes, the minimap can be a very useful tool to navigate around and to find the scene you want quickly.

Congrats! This completes the game. All the functionality is there and — as far as I can tell — there are no bugs to spoil the fun. You can find the project files for the finished app under 07-The-new-look in the Source Code folder.

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.
© 2024 Kodeco Inc.