VNUHCM
Bài trước Bài kế tiếp
  • Nội dung học
  • Trợ giúp
    Bạn có thắc mắc khi đang học?
    Hướng dẫn cách học Những câu hỏi thường gặp Email cho giáo vụ
    • Tiếng Việt
    • English
    • Thông tin Thành viên
    • Khoá học đăng ký
    • Đăng xuất
  • Cohota
  • HƯỚNG DẪN HỌC TẬP

  • Xem chi tiết >>
    Bạn đã hoàn thành 0% khoá học
  • HƯỚNG DẪN SINH VIÊN ĐĂNG NHẬP HỆ THỐNG
    • Hướng dẫn đăng nhập
    • Hướng dẫn vào khóa học
  • Introduction
    • Welcome
  • Unit 1: Values
    • Introduction - Unit 1: Values
    • Get Started With Values
    • Play with Values
    • Playground Basics
    • Naming and Identifiers
    • Simulation
    • Strings
    • Constants and Variables
    • Word Games
    • Build a PhotoFrame App
    • Design for People
  • Episode 1: The TV Club
    • Introduction - Episode 1: The TV Club
    • Searching for Content
    • Sharing Personal Information
    • Ordering Online
    • Reflection: Episode 1
  • Unit 2: Algorithms
    • Introduction - Unit 2: Algorithms
    • Get Started with Algorithms
    • Play with Programs
    • Functions
    • Types
    • Parameters and Results
    • Making Decisions
    • BoogieBot
    • Data Visualization
    • Build a QuestionBot App
    • Design an Experience
  • Episode 2: The Viewing Party
    • Introduction - Episode 2: The Viewing Party
    • Accessing the Show
    • Streaming on the Network
    • Reflection: Episode 2
  • Unit 3: Organizing Data
    • Introduction - Unit 3: Organizing Data
    • Get Started with Organizing Data
    • Play with Complex Data
    • Instances, Methods, and Properties
    • Arrays and Loops
    • Structures
    • Enums and Switch
    • Testing Code
    • Processing Data
    • Pixel Art
    • Password Security
    • Visualization Revisited
    • Build a BouncyBall App
    • Design a Prototype
  • Episode 3: Sharing Photos
    • Introduction - Episode 3: Sharing Photos
    • Capturing Images
    • Posting on Social Media
    • Reflection: Episode 3
  • Unit 4: Building Apps
    • Introduction - Unit 4: Building Apps
    • Get Started with App Development
    • Play with App Components
    • Color Picker
    • ChatBot
    • Rock, Paper, Scissors
    • MemeMaker
    • Build an ElementQuiz App
    • Design for Impact
  • Appendix
    • Episode Technical Concepts
    • Glossary
Tổng quan điểm khóa học
Đánh giá

Tiến độ
Tên tiêu chí Trọng số (%) Điểm Tiến độ (%)
Unit 4: Building Apps

Color Picker

Unit 4: Building Apps|Play

What you'll build

A color-picker app.

What you'll learn

  • How to connect your app's interface to your Swift code.
  • How to change your app's display from Swift code.
  • How to use switches, sliders, and a button in your app interface.

Key vocabulary

  • Action
  • CGFloat
  • Enabled, Disabled
  • Outlet
  • Slider
  • Switch

Introduction

When you develop an app, you write Swift code and build your UI in a storyboard. In order for the two to work together, they must be connected with actions and outlets. In this lesson, you’ll learn how to connect Swift code to the views and controls you create in a storyboard, so your code is able to respond to the user’s actions. Your app sees these actions as events—information about what happened and when it happened.

There are two kinds of connections between storyboards and code:

Outlets connect variables in your code to objects in the storyboard, so you can access those objects from your code and get information or make changes to them when the app is running.

Actions connect controls like switches and buttons to methods in your code, so tapping a button, for example, will run a particular method.

Your project will be to use outlets and actions to create an app that generates colors by mixing red, green, and blue. You'll use incremental development to make your work manageable. You first encountered incremental development in the BouncyBall app, and it's equally important in this complex project.

Part one: Learn how to use outlets to connect objects in the storyboard to your code, so you can access them when your app is running.

Part two: Learn how to connect actions from a switch in the storyboard to your code, so that a piece of code is run when the user changes the switch.

Part three: Create multiple switches, and use actions and outlets to display a color based on the on/off status of each switch.

Part four: Add sliders to allow more precise color control.

Part five: Add a reset button to set the switches and sliders back to their original values.

Part six: Polish the user interface to make the purpose of the switches and sliders easy to understand.

Part 1 - Creating Outlets

Outlets are a way for your code to control your app's interface. In this part, you’ll add a view to the scene in your storyboard, define an outlet in your Swift code that connects to the view, and use the outlet to set the background color of the view in code. When you’re done, your functioning app will look like this.

App with single black rectangle on it

Adding the Color View and Its Outlet

Follow these steps to get started:

  1. Create a new Xcode project using the App template located within the iOS tab: Choose the App template

  2. Name the app "ColorMix" and adjust the project settings as shown: App project settings

  3. Using the project navigator, open the Main storyboard.

  4. Click the Devices icon at the bottom of the window and select iPhone 14 Pro from the popup that appears. This allows you to see your interface layout as it will appear on the iPhone 14 Pro simulator.

  5. Find a view in the Object library. Remember you can use the search field to narrow down the list of options: A View in the object library

  6. Drag the view onto the view controller scene. Drag it near the top of the scene, then use the blue guides to center it horizontally.

  7. A white view on a white background is hard to see. Choose Editor > Canvas > Bounds Rectangles to see an outline of everything in the scene. View added to the scene

  8. Open the assistant editor. Make sure you can see ViewController in the assistant editor; if not, check that the jump bar at the top of the assistant editor is set to Automatic. 1

    Main storyboard with assistant editor open

  9. Control-drag (hold down the Control key, then click and drag with the mouse) from the view you added into the code.

  10. Make sure Outlet is chosen in the Connection pop-up menu.

  11. Enter colorView in the Name field.

  12. Click Connect.

Examining Your Code

Outlet declaration

Here’s what you’re seeing, from left to right:

  • Circle: The filled circle indicates that the outlet is connected. If the outlet wasn't connected, it would be an empty circle.
  • @IBOutlet weak: This is a signal to Xcode that the property on this line is an outlet.
  • var colorView: This is the declaration of a property you are already familiar with.
  • : UIView!: The type of the property is UIView!. The exclamation point means that if the outlet isn't connected and you try to access this property, your app will crash. UIView is the basic view type used in all iOS apps. Almost everything you see on the screen is a kind of UIView, which is responsible for drawing and handling touches.

The important thing to remember is that when you use colorView in code within ViewController, you're referring to the view you’ve just added to your storyboard.

Setting the View's Color

Find the function viewDidLoad(). This function is called when your view controller is ready to appear on the screen. Inside it, add the following code:

colorView.backgroundColor = .black

When the items from the storyboard have all been created and the outlets and actions have all been connected, viewDidLoad() is called. The code above sets the background color of the view to black. The view you see in the storyboard doesn’t change color, because the code doesn't run until the app is run.

Now build and run the app on the iPhone 14 Pro simulator. You’ll see a black rectangle at the top of the screen.

Troubleshooting Disconnected Outlets

Sometimes you’ll build and run your app with no errors, but the app immediately crashes on launch. Your device (or the simulator) shows your Home screen with an error in the console, like this:

"*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<YourApp.ViewController 0x7f8378f05b00> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key someNameFromYourApp"

This is a common error when you're working with actions and outlets. Unfortunately, the error message doesn’t tell you that the problem actually stems from a view in Interface Builder, but that’s exactly where you need to look.

You can practice this now by following the instructions below, or you can skip this section and move on to Part 2. (You might want to come back later if you encounter this bug as you're developing ColorMix.)

It's not unusual to change the name of a variable in your code. Perhaps you misspelled it, or you thought of a better name. Do that now for the colorView property. Perhaps colorSwatch is a more descriptive name. Change the name where it's declared and where it's used in viewDidLoad().

Now try to run your app. It should crash with an error as illustrated below. So how do you fix this problem?

App crashed in AppDelegate with uncaught exception

Go back to your storyboard. You can use the navigator (make sure to select the project navigator tab 1 at the top), or you can use the back button 􀯶 on the left of the jump bar 2 to navigate back to the file that was showing before the crash.

Select View Controller, either in the Document Outline 1 or by selecting the yellow circle at the top of the main editor in Interface Builder. 2

Now open the Connections inspector on the right side of the screen. Near the top of the Connections inspector, you'll see all the outlet connections for the selected view controller. 3

In the list of outlets, you should see both colorView and colorSwatch. colorSwatch isn't connected, and colorView has a little yellow hazard sign with an exclamation point. Interface Builder is warning you that it has a connection that doesn't match your code. Renaming an outlet property doesn't automatically update the connection in your storyboard. That's why you see both properties in the list. The mismatch won’t be discovered until the app launches and tries to hook up all its connections—at which point, the storyboard file will try to open a connection to a name that no longer exists. And your app will crash.

You can remove invalid connections by clicking the “x” next to the element’s name. (You can remove valid connections this way, too.) Do that now. The colorView outlet should disappear entirely. To connect the new colorSwatch outlet to the view, click and drag from the empty circle on the right of the outlet to the view in the storyboard.

Run the app again. This time, it should once again launch and function properly.

Note

Be sure to restore the name of the outlet back to colorView so you can continue following the lesson. This is a great way to practice the technique you just learned.

You're likely to encounter this kind of crash multiple times in your work. Even experienced iOS developers stumble over it occasionally.

Next, you'll be learning about actions. Mismatched actions can cause the same kinds of crashes if you change your code without updating the storyboard. And they can be fixed in a similar way.

Summary

In this part of the lesson you started the incremental development of ColorMix by putting a view onscreen to display a color. You’ve learned how to:

  • Use the assistant editor to show your code side-by-side with the storyboard.
  • Use Control-drag from the storyboard to your code to create and connect outlets.
  • Use outlets within your code to change the properties of objects created in the storyboard.

Part 2 - Creating Actions

To make buttons, switches, and other controls useful in an app, something has to happen when the user interacts with them. This is done by creating actions. An action runs your code when the user does something. For example:

When the value of a switch is changed (from off to on, or from on to off), call switchChanged(_:)... on this ViewController.

In this part, you'll add a UISwitch control. You'll define an action method and connect it to the switch. When the switch is tapped, your code will change the color of the color view. When you’re done, your app will look like this.

App with a switch and a red rectangle

Follow the steps below to create an action:

  1. In Xcode, open the Main storyboard.
  2. Find a switch in the Object library and drag it onto the scene. Use the guides to center it horizontally and vertically 1: Dropping a switch in the center using the guides
  3. In the Attributes inspector set the Value of the switch to Off 1: Switch being set to Off in the attributes inspector
  4. Open the assistant editor so that ViewController is visible.
  5. Control-drag from the switch into the code file and release.
  6. Make sure Action is selected in the Connection pop-up menu.
  7. Enter switchChanged in the Name field.
  8. Enter "UISwitch" in the type field. The pop-up menu should look like this 1: Configuring a switch action
  9. Click Connect.

A new method is created for you inside the ViewController class. It looks much like other functions and methods you've seen and created, with a few additions.

Declaration of action method

Here are some details, from left to right:

  • Circle: The filled circle indicates that the action is connected.
  • @IBAction: This line signals to Xcode that the method on this line is an action connected to a control in Interface Builder.
  • sender: The sender argument is the UI element that initiated the action. You chose the type UISwitch, since you know that's the type of storyboard object connected to this IBAction.

Add the following code inside the method:

colorView.backgroundColor = .red

Build and run the app. When you turn the switch on, the view changes to red. But when you turn it off, it stays red. What’s going on? This is because the action method is called every time the value of the switch changes, and the action method always sets the background color to red.

To make the switch behave like a real switch, delete the line you added above and add the following code:

if sender.isOn {
    colorView.backgroundColor = .red
} else {
    colorView.backgroundColor = .black
}

isOn is a Bool property of UISwitch that describes if the switch is on or not. The updated version of this method now checks the state of the switch (which is available in the sender parameter of the function) and sets the color appropriately.

Summary

In this part of the lesson you’ve learned:

  • How to connect controls in the storyboard to action methods in your code.
  • How to get information about the control from the sender argument.
  • How to use UISwitch.

Part 3 - Multiple Actions and Outlets

When you see color on the screen, you’re actually seeing three components: red, green, and blue. In this part of the lesson, you’ll add two more switches to the app, so you’ll have one switch for red, one for green, and one for blue. Switching each one on or off will add that color component to the color displayed on the screen.

To make this work, you'll need to create outlets for each switch, so that the isOn property of each switch can be checked when creating the color to be displayed. At the end, the app will look like this.

Screenshot of app with three switches and a magenta block

Adding More Switches

Follow these steps to add the new switches:

  1. In Xcode, go to the Main storyboard.
  2. Select the switch.
  3. Type Command-D to duplicate the switch. Drag it underneath the first one, using the guides to line them up.
  4. Repeat step 3 to add a third switch.
  5. Open the assistant editor to show ViewController.
  6. Control-drag from each switch to the code file to create outlets. Call them redSwitch, greenSwitch, and blueSwitch.

A common technique is to connect multiple storyboard objects to the same action in your code. Select the middle switch and open the Connections inspector in the inspector area.

The image below shows all of the outlets and actions the selected switch is connected to. 1

Connections inspector showing connected outlets and actions

In Sent Events, you can see that the Value Changed event is linked to switchChanged: in ViewController. Although you could link to the other events, only the Value Changed event is sent when a switch is tapped.

Referencing Outlets shows that the switch is referenced by greenSwitch in ViewController. Because you duplicated the red switch instead of adding new ones, it's already connected to the switchChanged action.

Follow these steps to learn another way of connecting actions:

  1. In the Connections inspector click the X button by the Value Changed event. This will disconnect the green switch from the action.
  2. In the ViewController file in the assistant editor, find the circle icon in the gutter next to the action method. When you mouse over it, the red switch and blue switch will be highlighted in the storyboard to indicate that they’re connected.
  3. Drag from the circle onto the green switch to reconnect the action.

This video shows the steps:

There are many ways to connect outlets and actions. Control-dragging from the storyboard to code can create new outlets and actions. Once they're in place, they can be connected, disconnected, and reconnected from the Connections inspector or from the circle icons in code files.

Which is better? Use the Connections inspector if you want to know which outlets and actions a particular object in the storyboard is connected to. Use the circle icons in code if you want to know which objects a particular outlet or action is connected to.

Updating Your Code

Now that you’ve created the user interface and connected all the outlets and actions, it’s time to write the code.

The action method is not the best place to update the color of the view. There's already some repeated code in the file—the color is set to black in viewDidLoad and again when the switch is turned off. A better design is to have a single function that makes the correct color based on the state of each switch, and to call this from the action method and from viewDidLoad. That way, there's no repeated logic. If different actions are added that can also update the color, they can call this new function as well.

Add the following code to the ViewController class:

func updateColor() {
    var red: CGFloat = 0
    var green: CGFloat = 0
    var blue: CGFloat = 0
    if redSwitch.isOn {
        red = 1
    }
    if greenSwitch.isOn {
        green = 1
    }
    if blueSwitch.isOn {
        blue = 1
    }

    let color = UIColor(red: red, green: green, blue: blue, alpha: 1)
    colorView.backgroundColor = color
}

This method starts with three variables representing the red, green, and blue components of a color, initialized to 0. It checks each switch value and sets the component to 1 if the switch is on. Finally, a color is created from the components, and the color of the colorView is set.

To update the action method to call this new function, remove the code that was setting the color and replace it with the single line updateColor(). From viewDidLoad(), remove the line that was setting the color and replace it with updateColor().

Build and run the app. Change each of the switches, and you’ll see the colors mix together.

Reminder: Troubleshooting Problems with Actions and Outlets

When you make an outlet or action, you're making changes in two places. You add the @IBOutlet or @IBAction line to the Swift code, and you add information to the storyboard file that links the view with the outlet or links the control event with the action.

If you delete the code part of the outlet or action but don’t change the storyboard, your app will likely crash. When the outlet is assigned or the action is fired, there won't be any code matching it—a crashing error. To resolve the error, you must make sure both to disconnect unwanted actions and outlets from the storyboard and to delete them from your code.

You can always refer back to the Troubleshooting Disconnected Outlets section if you need help.

Summary

In this part of the lesson, you’ve learned:

  • That a single object can be connected to both outlets and actions.
  • How to use the Connections inspector to check outlets and actions.
  • How to disconnect outlets and actions using the Connections inspector.
  • How to link an object to an existing outlet or action using the circle icon in the gutter of a code file.

Part 4 - Sliders

Your three switches now control the three components that make up a color. But since each switch represents a binary choice, you can only make eight colors. In this part of the lesson, you’ll add slider controls—like the brightness or volume sliders on an iOS device—to provide finer control. At the end, your app will look like this image.

Screenshot showing sliders controlling the color of the view

Adjusting Your UI

First, you need to make some room in the interface for the sliders. You could put them all under the switches, but it would make more sense for the slider to be next to the switch that controls it. So you need to move all the switches to the left, space them out a little, then add a slider next to each switch.

Follow these steps:

  1. Select all three switches by Shift-clicking each one or by dragging a selection box around them.
  2. Drag the group of switches over to the left and up, so that the central switch is vertically centered in the scene and all three switches are aligned to the left margin guide. This video shows you the steps:
  3. Select the top switch only, then hold down the Shift key and press the Up Arrow key four times to move it farther from the central switch.
  4. Select the bottom switch, then hold down the Shift key and press the Down Arrow key four times to move the switch farther down.

Why add space between the switches? At the moment, you’re using the simulator with a cursor, which allows you to hit a very small object with precision. But if you were using a device with your finger, you'd need more room around interface elements.

Follow these steps to add the sliders:

  1. Find a slider in the Object library and drag it onto the scene. Using the guides, align it vertically with the top switch, and horizontally within the scene.
  2. Repeat this for the other switches, so each switch has a slider next to it. 1
  3. Drag or Shift-click to select all three sliders.
  4. In the Attributes inspector, set the Value to 1. Note a slider also has a minimum and maximum value. 2

Attributes inspector with a slider selected

Connecting Outlets and Actions

Follow these steps to create new outlets for the sliders.

  1. Open the Assistant editor so ViewController is showing.
  2. Control-drag from each slider into the file to create outlets. Name them redSlider, greenSlider, and blueSlider.

Follow these steps to connect the sliders to actions. This video shows the details:

  1. Control-drag from the top slider into the file to create an action, called sliderChanged.
  2. Drag from the circle icon in the gutter by the new action to each of the other two sliders, so that all sliders are connected to the same action.

The sliderChanged action method is simple. Just call updateColor(), the same as the switchChanged action method.

But the updateColor() method needs updating. Currently, it sets the value of each color component to 1 if the switch is on.

You'll need to use the value property of each slider, which is set to return a value between 0 and 1.

Update each line so that it uses the value of the corresponding slider instead. This example is for the red switch. Follow the same pattern to update the other two switches:

if redSwitch.isOn {
    red = CGFloat(redSlider.value)
}

redSlider.value returns a value of type Float, but to make a UIColor, you need a CGFloat. The code above creates a new CGFloat from the Float value of the slider and assigns it to red.

When you’ve updated the code for the green and blue switches, run the project again. You can now adjust the levels of each color component, so you’re set to make any color you like.

Summary

In this part, you’ve learned:

  • How to select a group of items in Interface Builder.
  • How to move items precisely using the cursor keys.
  • How to use UISlider.

Part 5 - Reset Button

One of the most common controls in iOS apps is a button. A button can contain text, an image, or a mix of both. When the user taps the button, something happens. In this part of the lesson, you’ll add a Reset button, which will be handy if you accidentally pick a really horrible color. At the end, your app will look like this.

Screenshot showing the app with a new Reset button

Adding the Button

Open the Main storyboard and find a button in the Object library. Drag it onto your scene, using the guides to center it horizontally, and place it near the bottom of the screen.

Select the button and change the title to "Reset" by double-clicking it and editing the text, or by using the Attributes inspector. 1

The button should look like this. 2

Reset button in position at the bottom of the scene

Open the assistant editor, making sure it's displaying ViewController, and Control-drag from the button to the code file to create an action. Name the action reset. It should look like this:

@IBAction func reset(_ sender: Any) {

}

Open the Connections inspector. You’ll see that the button has been connected to the Touch Up Inside event. This is the standard event used for most buttons.

Updating the Code

Your Reset button will set the value of each slider to 1 and the isOn property of each switch to false. Add that code to the new action method.

You’ve updated the switches and sliders using code, but you want the same behavior as if the user had changed the switches. When the user changes a slider or a switch, the color displayed on the screen updates. To have the same behavior occur when the Reset button is tapped, add a call to updateColor() at the end of the action method. 

Build and run the app, set a color, then try the Reset button.

Summary

In this part, you’ve learned:

  • How to add a button to the storyboard and change its title.
  • How to use the Connections inspector to see which control event an action is connected to.
  • Which control event is usually used for buttons.

Part 6: Polishing the Interface

Your app is working well now, but there are some things about the interface that could use improvement:

  • All the switches and sliders look the same. There’s no way to tell what control does what without using it.
  • When all components are on, the color view disappears into the background.
  • If a switch is off, the slider can still be operated, but it doesn’t do anything—which may be confusing for the user.

At the end of this part, your app will look like this.

Screenshot of final project

Tinting the Switches

To correct the first issue, you can use the tinting options available for many controls. A switch can have two custom colors applied, the On Tint and the Thumb Tint, which affect the following areas of the switch:

Diagram showing how tinting affects the switch

Now change the on tint for each switch to match the color it controls. Follow these steps to make the purpose of your switches clearer:

  1. In Xcode, open the Main storyboard and select the red switch.

  2. Open the Attributes inspector. Under the Switch heading are two tint options. Change the value in the On Tint pop-up menu. 1

    Attributes inspector for a switch showing an on tint color

  3. Select System Red Color for this switch.

  4. For each of the other two switches, follow the same steps to select System Green and System Blue.

You’ll notice that the scene doesn’t change when you change the colors. That’s because the switches are all set to off.

Tinting the Sliders

Sliders have three tinting options. You can set the color of the track (the line the handle moves along) on the low and high sides of the thumb, and on the thumb itself.

Diagram showing how tinting affects a slider

Select one slider at a time, then use the Attributes inspector to set the Min Track color to be red, blue, or green, as appropriate. 1

Attributes inspector for a slider showing tint options

With these changes, it will be clearer what each switch and each slider will control in the color view.

Adding a Border

To make the color view stand out from the background, regardless of the currently selected color, you can add a border to the view.

This can be done in code. Switch to ViewController and add the following lines to viewDidLoad():

colorView.layer.borderWidth = 5
colorView.layer.cornerRadius = 20
colorView.layer.borderColor = UIColor.black.cgColor

This code uses the layer property of a UIView, which in turn has its own properties. This code creates a black border, five points wide, with rounded corners.

Disabling Sliders

For your final refinement, you want to prevent each slider from working when its associated switch isn’t turned on. All controls have a property, isEnabled, that allows the control to be activated or deactivated. The status of a control is accompanied by a change in its appearance, such as a dimming effect.

Add a new method to make sure all the sliders are enabled properly:

func updateControls() {
    redSlider.isEnabled = redSwitch.isOn
    greenSlider.isEnabled = greenSwitch.isOn
    blueSlider.isEnabled = blueSwitch.isOn
}

This function has to be called whenever a switch is changed. Inside the switchChanged action, add a line that calls it:

updateControls()

Then add a call from viewDidLoad() so that the sliders are disabled correctly when the app first launches. Also add a call from reset(_:) so it works when you reset the app.

Build and run the app. You’ll notice that the sliders can’t be used until the switches are turned on. And when each switch is on, its color matches the color of the slider.

Summary

In this part, you’ve learned:

  • How to customize the look of switches and sliders in Interface Builder.
  • How to add a border and rounded corners.
  • How to enable and disable controls that aren't currently used.

Reflection Questions

Look at an app that you use every day.

Which parts need to be hooked up to code logic in order to work? Which parts are static or decorative elements that don't change on the screen?

Identify the kinds of inputs the app supports.

How does the user input information?

What kinds of data does the app gather from the user?

Summary

In this lesson, you covered the basics of making your app's user interface come alive. You created a way for your Swift code to modify user interface elements via outlets, and you caused your user interface controls to execute Swift methods through actions. You connected switches, sliders, and a button to make a fully interactive app.

In the next lesson, you'll revisit the QuestionBot app by turning it into ChatBot, which can handle entire conversations in a scrolling list.

Báo lỗi