IOS Spellbook 1
IOS Spellbook 1
IOS Spellbook 1
Spellbook
Table of Contents
1. Introduction
2. First Steps
3. App Anatomy
4. Views
5. Working with Colors
6. Animations
7. Touch Events and Gesture Recognizers
8. Simple UI Components
9. Auto Layout
10. View Controllers and Controller Hierarchy
2
iOS Spellbook
iOS Spellbook
The book is still under development. This is just part 1 of 3. The whole book
has over 600 pages. More comming soon!
Version 0.1
Introduction 3
iOS Spellbook
Introduction 4
iOS Spellbook
Dedication
I dedicate this book to my amazing son Amon, and my lovely wife Zsu.
Thank you for bringing so much joy in my life!
Introduction 5
iOS Spellbook
Introduction 6
iOS Spellbook
Welcome
Have fun!
Introduction 7
iOS Spellbook
Introduction 8
iOS Spellbook
The assumption is that you know how to code in swift and that you
understand basic Object Oriented Programming concepts. If you don't, then
you can learn programming in Swift from Swift Programming from
Scratch(book) and Object Oriented Programming in Swift(article).
Alongside the theory, you will find various exercises and projects. Solving
them will help you solidify what you learn. You will also learn how to use
different tools and workflows
Introduction 9
iOS Spellbook
1) The Fundamentals
This part teaches you the first body of knowledge you need to start your iOS
developer journey. With this knowledge you can make simple apps and
learn to use different parts that make up more complex apps - like the one
you want to build :)
2) Exploring iOS
In this part we are going to learn how to use different SDKs like the camera,
accelerometer, notifications and so on. We are also going to study more
complex UI components like table views and collection views.
In this part we'll focus more on the other side of iOS development. How do
you store your code, deploy or test your app. What tools do you use? How
do you use open source? How do you give people your app and how to
submit it on the app store
After reading the first part in order, you can pretty much take any path you
desire until you finish the book. Some chapters require that others must
have been previously read, in that case they will be indicated on the first
page of that chapter.
Introduction 10
iOS Spellbook
Introduction 11
iOS Spellbook
First Steps
So you know how to code and you want to learn how to make iOS apps.
The first thing you need to learn is how to use the tools of the trade:
Xcode is by far the most important tool for creating iOS apps. It's the first
and only tool you need to learn to start making basic apps. In the first part of
the book we are going to learn how to use Xcode to create apps and
understand what an app is and how it works by studing it basic
components.
Instead of listing every feature of Xcode has I'm going to take you step by
step and make a simple iOS app. The goal of this chapter is to give you a
taste of iOS development and to showcase the main features of Xcode.
First Steps 12
iOS Spellbook
The app has two interface components: a label and a button. The label
displays the number of times the button has been pushed. The logic has a
small twist, it shows an emoji every 3 taps. Hence the name "Tap. Tap.
Boom!".
First Steps 13
iOS Spellbook
That's it! Let's see how the app looks at this point. Run the project by
pressing on the play icon in the top left corner of Xcode.
The iOS simulator should start booting up. After it finishes you should see
an empty white screen.
Quick tip
In some cases the height of the simulator is bigger than your screen.
You can resise the simulator by using the following shortcuts:
First Steps 14
iOS Spellbook
In the navigator find and click on Main.storyboard . After that the middle
part of Xcode should be replaced with Interface Builder showing an
empty screen.
Interface Builder is the tool used to create interfaces. Starting with iOS 8
it's possible to make components that render in Interface Builder. So even if
you decide to use code to make a part of your interface, you can still use it
in Interface Builder .
First Steps 15
iOS Spellbook
Quick tip
You you need a custom control that Apple does not provide before
starting to implement it search github and CococaControls for it.
The default simulator is the iPhone 7 Plus one. You can change the
simulated device from the menu on the top left side.
After clicking on iPhone 7 Plus icon, you will be shown a menu. If you have
any devices connected it will show them in this list. That takes a few steps
to setup, so for now let's stick with the simulator.
First Steps 16
iOS Spellbook
Quick tip
First Steps 17
iOS Spellbook
Let's add the first component. Find the Component Library in the bottom
right corner of Xcode.
The default is the Library so you might have to change the tab by
pressing button:
Using the filter function you can search for button. Drag and drop the button
- change the title to Tap Me! by double clicking on it.
First Steps 18
iOS Spellbook
Now search for label in the Component Library . Drag and drop a label on
the screen.
First Steps 19
iOS Spellbook
Step 5: Test
Run the app to see if the components are showing correctly, this time do it
by pressing Command + R ( ⌘ + R ).
First Steps 20
iOS Spellbook
First thing make some space. Hide the Document Outline by pressing the
button in the lower left corner of Xcode.
First Steps 21
iOS Spellbook
Now you should have the interface on the left side and the code on the right
side.
First Steps 22
iOS Spellbook
On the right side you should see the ViewController class. A view
controller is an object that manages a view. The usual case is one screen
has one view controller. More on this in the View Controller chapter. For
now, just remember that view controllers have the code for a screen.
Right click on the button and drag (keep holding) to connect the
touchUpInside: event to a line inside ViewController class.
First Steps 23
iOS Spellbook
First Steps 24
iOS Spellbook
This will create a new method named didTapButton(_:) that will be called
when the button is tapped. The parameter sent to it is the button that was
pressed. So you can have multiple buttons send the same action - for
example in a calculator app all the digits will be connected with
didTapDigit(_:) .
Run the app to test your code. After tapping the button, a message should
be displayed in the console from the Debug Area . The console will open by
itself in the moment it has something to show.
You can show/hide the Debug Area from the menu View > Debug Area >
First Steps 25
iOS Spellbook
Ok... consoles are super fun, now let's update something on the screen.
To change the text inside the label we need to get a reference to it. We can
do this by creating a Referencing Outlet.
Almost same process we had with the button action. Right click on the label
to display a menu:
Connect the New Referencing Outlet option with the top lines of the view
controller:
First Steps 26
iOS Spellbook
...
}
var counter = 0
...
}
First Steps 27
iOS Spellbook
...
To fix the first problem for in Interface Builder and select the label. In the
Attributes Inspector you set the text alignment property to centred.
To fix the second one create a unit string constant before setting the text.
unit will be "tap" if the counter is 1 and "taps" otherwise.
...
First Steps 28
iOS Spellbook
if counter == 1 {
unit = "tap"
} else {
unit = "taps"
}
And now for the explosive effect: if the counter is divisible by 3 then replace
the text with Boom! .
...
if counter == 1 {
unit = "tap"
} else {
unit = "taps"
}
if counter % 3 == 0 {
label.text = "Boom!"
} else {
label.text = "\(counter) \(unit)"
}
}
}
Tap, Tap, Boom! Tap, Tap, Boom! Tap, Tap, Boom! ...
First Steps 29
iOS Spellbook
Conclusion
Xcode is one of the most loved and intelligent programming environment.
It’s really extremely easy to use it, and it’s the only tool you need to know for
creating beautiful and powerful Apps! It helps you do everything from writing
code to creating the interface and running the app in simulator or on your
device.
First Steps 30
iOS Spellbook
App Anatomy
So you kinda know what's an app. It's that round cornered square on your
phone. But what makes it tick?
In this chapter we are going to go over a few low level components that
make an app. You don't have to understand every detail mentioned here so
don't panic if you can't follow some of them. The purpose of this chapter is
to show you what ingredients are used to make an app, how does the
process of transforming them in to an app looks like and what technically
what an app really is.
App Anatomy 31
iOS Spellbook
What's an app?
A computer system (or phone) runs all kinds of code. Three kinds to be
precise:
1) Kernel - this is the code that gives a programable interface for the
hardware.
App Anatomy 32
iOS Spellbook
The Assets file holds your app's resources. For example the app icon.
The Info.plist file is used to configure the app. It holds information about
the app's behaviour that can be interpreted by iOS without running the app.
For example the version of the app is store in this file.
App Anatomy 33
iOS Spellbook
Almost every time you want to use new feature in your app, you will have to
update this file. For example, if you use the camera, you will have to add a
message in Info.plist . That message will be displayed by the system
when asking for permission to use the camera.
Main.storyboard is the main interface file. It defines the first screens the
app will load. The first that gets shown is called Initial View Controller
and it is marked with an arrow starts from nothing and points to it.
ViewController.swift is the swift file that describes the class on the main
App Anatomy 34
iOS Spellbook
Xcode Project
Another file is the project file - the blue one on the top with the name of the
project. If you click on it, you will see this menu:
App Anatomy 35
iOS Spellbook
Notice that the list on the left has two categories: Project and Targets . A
project is a collection of files. To make an executable you have to define a
target. A target is a subset of files from a project that get built together to
make an executable/app - or a library.
If you click on the default target the menu on top will change and display
more tabs.
These tabs lets you configure the way your files get built in to an app:
of the application, version and build number, the main interface file, the
launch screen interface file, supported orientations and much more...
Capabilities - this view lets you manage special features your app uses.
For example: Push Notifications, In App Purchases, Game Center, Siri,
Apple Pay, HealtKit, Maps.
Build Settings - lets you configure, at a low level, the way the code gets
compiled and built.
Build Phases - this shows the phases the files go trough when the project
is building
App Anatomy 36
iOS Spellbook
The first phase is Target Dependencies . If other targets are present here,
they will get build before the current one. This lets you include a library into
your project.
The second one is Compile Sources . This one compiles all swift files and
links them together to form one binary file.
In the Link Binary With Libraries phase, the binary created in the
previous one is "glued" with system libraries that provide extra features (e.g.
Maps, Apple Pay)
In the Copy Bundle Resources phase, all resources used by your project get
copied in the app Resources folder.
App Anatomy 37
iOS Spellbook
Build Rules - here you can see a list of rules for handling different file
types. For example, storyboards and interface files are compiled to a binary
format that is more efficient in term of storage than the one you use with
interface builder. There's one rule that specifies that .plist files get copied
and another one that compresses images before adding them in the
Resources folder. A lot of neat things happen while building an app - you
can read more examples by scrolling in the Build Rules tab.
When you press play in Xcode you actually build the current target and
upload it on your testing device or simulator, then you run it.
App Anatomy 38
iOS Spellbook
UIApplication
A thing is missing from the Swift project template. It's the main.m file that
you can see if you create a new project with the language set to Objective-
C:
#import
#import "AppDelegate.h"
This is the last piece of the puzzle. After we untangle these lines of code
you will understand how an app ticks!
App Anatomy 39
iOS Spellbook
#import
#import "AppDelegate.h"
The first one imports the UIKit framework and the second one imports the
AppDelegate interface - this lets the code use UIKit and AppDelegate .
Another thing about iOS is that it's actually UNIX at its core. Just like
macOS, tvOS and watchOS.
In C, code execution starts from the main function, not from the first line.
That's the starting point of all C programs. The main function gets a variable
list of parameters. This comes from the fact that UNIX systems are operated
using a console, you can invoke programs by typing the name of the
program, followed by parameters or flags:
> add 1 2
> 3
That's the start of the UNIX program, so the line aftr will be the first one that
gets executed. FYI argc is the number of parameters and argv is and
array that holds them.
App Anatomy 40
iOS Spellbook
That's how the app handles memory management - all the objects created
between { and } are registered in the same pool. That pool keeps track
of what objects are used or not, so they can be removed from memory.
It looks in the Info.plist file and notices that you are using
LaunchScreen.storyboard as the launch screen - it will quickly display it on
the screen while it loads your first view controller from Main.storyboard .
The AppDelegate receives general app events. Things like the app started,
the app will/did go in the background, the app will/did come back from the
background, the app will close. A more detailed description of the events
can be found in Apple's reference.
That was the magic behind the curtain. I hope I didn't bore you to death with
all the low level details. Anyway, that's how an app ticks!
App Anatomy 41
iOS Spellbook
TA DA!
To generate the app you can either run the app with Command + R ( ⌘ + R ) or
just build it with Command + B ( ⌘ + B ). After that you can find the application
in the Products group:
App Anatomy 42
iOS Spellbook
To show the contents of the app folder right click and select Show Package
Contents :
App Anatomy 43
iOS Spellbook
If the app would have any resources, it would have a Resources folder
containing them
App Anatomy 44
iOS Spellbook
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func applicationWillResignActive(
_ application: UIApplication) {
print("mom's calling")
}
func applicationDidEnterBackground(
_ application: UIApplication) {
print("goodbye, for now.")
}
func applicationWillEnterForeground(
_ application: UIApplication) {
print("darn it!")
}
func applicationDidBecomeActive(
_ application: UIApplication) {
print("hello!")
}
func applicationWillTerminate(
_ application: UIApplication) {
print("bye!")
}
}
App Anatomy 45
iOS Spellbook
App Anatomy 46
iOS Spellbook
UIView Fundamentals
Now that we took a look at how the internal organs of an app work, we can
start learning about its skin. Every time you look at your iPhone you see
UIViews. UIViews everywhere!
In this chapter we are going to see what an UIView is, what it can do and
how you can use it to make your apps.
Views 101
The UIView class defines a rectangular area on the screen on which it
shows its content. It handles the rendering of any content in its area and
also any interactions with that content - touches and gestures.
The most basic thing a view can do is to fill its area with a color.
Starter Project
Views 47
iOS Spellbook
You can change the background color from the left side menu:
Views 48
iOS Spellbook
Awesome!
Now let's see what actually happened when we changed the view. The first
Views 49
iOS Spellbook
thing we did was to make the view taller and then move it around. Views
draw content in a rectangular area. The size and position of that area are
stored in the frame property. The frame has an origin - the position of the
top left corner - and a size - the width and height of the view.
You can find the frame by opening the Size Inspector . Make sure your
Utilities Pane is visible.
After that we changed the color into yellow. This actually sets the
backgroundColor property of the view to a yellow color and that will make
the view draw a yellow background.
Coordinate system
To understand how a frame represents a rectangle on the screen, we have
to take a look at the coordinate system used by its views.
The red square has the origin in (x: 200, y: 200) and the size (width:
Views 50
iOS Spellbook
The top left corner of the screen is the point (0, 0) or CGPoint.zero . In
iOS we represent points using the CGPoint struct. The horizontal axis is the
X axis and the vertical one is the Y axis . The more to the right a point is,
the greater its x coordinate will be. The lower a point is on the screen, the
greater its y coordinate will be.
The red square has a width of 200 points . Can you guess the coordinates
of points A , B , C from the image above?
Views 51
iOS Spellbook
Answers:
A = (400, 200)
B = (200, 400)
C = (400, 400)
Views 52
iOS Spellbook
The object library contains all the types of objects that you can use to make
the interface of your app. Here are a few:
Views 53
iOS Spellbook
They are all subclasses of UIView . So all you learn about UIView applies
to all of the components you will use :)
In the bottom of the Object Library you will see a search field. Look for
Views 54
iOS Spellbook
UIView .
Views 55
iOS Spellbook
You will notice that the view is invisible. The default background color for a
view is transparent - or clear color. In order to see the view, you will need to
change the background color to something else.
Views 56
iOS Spellbook
Play around - see what other components you can add on the screen. We
are going to go over them in detail later in the book.
In iOS, the MVC pattern is implemented with a small twist; all controllers are
actually View-Controllers - controllers that have views. All view controllers
are a subclass of UIViewController and their view can be accessed by
Views 57
iOS Spellbook
import UIKit
Zen bits
The viewDidLoad method is called when the screen has loaded its
interface. In this case, whatever you have in your storyboard. User
interfaces can also be loaded from NIB files or directly from code.
viewDidLoad is the most common place where you will add you setup logic
for your screen.
Let's add another view to the screen. First we create a frame for it. Then we
instantiate a new UIView object for that frame. And to make it visible, we
Views 58
iOS Spellbook
are going to make the background color blue. After that we add it as a
subview to view .
view.addSubview(blueSquare)
}
...
}
If you run the app now, you are going to see something like this:
Views 59
iOS Spellbook
Nesting views
Before we did not explain a step: adding blueSquare as a subview to view
Views can have other views that are drawn inside them. They are called
subviews. The coordinate system of subviews has the origin (the point (0,
0) ) in the top left corner of the parent view - also called superview. The list
of subviews a view has can be accessed by calling it's subviews property.
You can also access the superview of a view by calling it's superview
property.
blueSquare.addSubview(blackSquare)
}
...
}
Views 60
iOS Spellbook
If you run the app now, your screen should look like this:
If you want to see the view hierarchy you can pause your app and take a
look.
First open the Debug Area . In the top right corner of Xcode you have a
segmented control that looks like this:
Run the app. In the lower mid section of Xcode find and click Debug View
Hierarchy button:
Views 61
iOS Spellbook
If you followed all the steps you should see an interactive 3D representation
of the view hierarchy. You can move it around by clicking and dragging.
Views 62
iOS Spellbook
Interface Outlets
After designing your interface you might need to access a few components
in order to populate them with content or add extra customisation.
Views 63
iOS Spellbook
declaration attribute.
...
}
Open Main.storyboard . Make sure you have the Document Outline pane
open - here you can see all of the objects form the interface.
Views 64
iOS Spellbook
Right click View Controller - it has a yellow icon. A popup should appear.
In it there is a list of outlets you can connect to the view controller. If you
look closely, you will spot the newly added redSquare outlet.
Views 65
iOS Spellbook
Views 66
iOS Spellbook
Great! Now that we have a reference to it, we can change its properties at
runtime.
redSquare.backgroundColor = .orange
...
}
}
If you followed with all the steps you should see an orange view instead of
the red one. You can change any view attribute at runtime and the changes
should immediately be visible.
Views 67
iOS Spellbook
Open the Assistant Editor by pressing the button with two circles.
Right Click on the view you want to add an outlet to. Then drag an drop the
outlet directly in the code of the view controller.
Give it a name and set the type if necessary. That will write the @IBOutlet
Views 68
iOS Spellbook
alpha
If the value is smaller than 1 , the view and all its subviews will become
transparent.
transform
You can apply affine transformations to a view.
view.transform = rotateAndShrink
Views 69
iOS Spellbook
I'm not going to go into detail on this topic. I will only mention two
transformations that you will likely use.
Scale
The scale transform has two factors - one for the x axis and another for the
y axis.
Rotate
The rotate transform rotates the view by a number of degrees. The value
given to CGAffineTransformMakeRotation is expressed in radians. A positive
value means a counterclockwise rotation and a negative value means a
clockwise rotation.
Views 70
iOS Spellbook
tag
The tag property is exactly what its name says. You can label a view with
an Int value. The default value is 0. This is useful when you have multiple
views with the same functionality (ex. the number buttons from a calculator
app - you can use the tag to extract the digit and all the logic is the same).
hidden
Sometimes you don't want to show a view, but you want it to remain in the
view hierarchy. Set the value of hidden to true to hide a view. All touch
events will be ignored while the view is hidden.
bounds
bounds represents the frame of the view in its own coordinate system, in
other words, a frame with the same size and origin in .zero .
Good question!
Views 71
iOS Spellbook
Why not?
let backgroundImage =
BackgroundImage(frame: view.bounds)
center
It gives you the coordinate of the view's center in the coordinate system of
its superview.
Maybe I like math too much, but if you like designing custom animations or
controls, you will definitely like this property.
Views 72
iOS Spellbook
The Layer
The view uses a CALayer object to render its components. It can be
accessed by calling the layer property.
While the layer is used to draw the view, it also has a few visual attributes
that you can use to customise the view. The most common ones are:
borderWidth
You can add a border to the drawing area by setting the borderWidth
view.layer.borderWidth = 1.0
view.layer.borderWidth = 10.0
Views 73
iOS Spellbook
cornerRadius
Ever wondered how those rounded corners are made? Search no more!
cornerRadius is what you are looking for!
view.layer.cornerRadius = 40.0
If you have a square view and you set cornerRadius to half the width you
Views 74
iOS Spellbook
borderColor
You can set the color of the border by setting the borderColor property to a
GCColor . You can convert any UIColor to a CGColor by calling its
CGColor property. (ex: UIColor.blackColor().CGColor )
view.layer.borderColor = UIColor.blueColor().CGColor
Views 75
iOS Spellbook
Shadows
shadowOpacity
Views 76
iOS Spellbook
shadowOffset
The shadow can be offset to give different effects based on the supposed
light might be. The reason the default shadow was so high is that the
default value of shadowOffset is (0, -3) .
shadowRadius
The shadow shows in a band round the layer. The width of that band is
called shadowRadus . The default value is 3.0 .
Views 77
iOS Spellbook
shadowColor
You can make the shadow have any color you want. I personally like purple
shadows - they are the best. No really - stick to black or gray.
Views 78
iOS Spellbook
Views 79
iOS Spellbook
IBDesignable
You can expose more customisation points in Interface Builder by making
certain properties IBInspectable and the class IBDesignable .
Marking a class IBDesignable tells interface builder to run the code from
that view and render it instead of showing a simulated version.
@IBDesignable
class BorderedView: UIView {
@IBInspectable var borderWidth: CGFloat = 0 {
didSet {
layer.borderWidth = borderWidth
}
}
}
To use it. Add a UIView in your screen. Select it. Then go to the Indentity
Inspector .
Then go to the Attributes Inspector . You should see the Border Width
field:
Views 80
iOS Spellbook
Conclusions
Although UIView might seem small, it's actually the blood and skin of iOS.
Knowing how to use it is critical if you don't want to reinvent the wheel.
There are a lot of customisation points for views, by combining them you
can avoid writing extra code and make better apps.
Views 81
iOS Spellbook
Exercises
1. Target
Solution
Views 82
iOS Spellbook
let width = 20
let padding = 20
circle.backgroundColor = UIColor.whiteColor()
circle.layer.borderColor = color.CGColor
circle.layer.borderWidth = CGFloat(width)
circle.layer.cornerRadius = CGFloat(l / 2)
parent.addSubview(circle)
parent = circle
l -= 2 * (width + padding)
}
2. Gradient
Make a gradient that goes from black to white using only UIViews .
(white).
Hint
Views 83
iOS Spellbook
Try adding views in a line and change their background color. If you make
them thin enough, it will look like a gradient.
Solution
Views 84
iOS Spellbook
x += stepSize
}
Warning: this is not a practical approach! In real life you would use an
image background that can stretch or CAGradientLayer.
3. UberView
Make a view that exposes the borderWidth , borderColor and
cornerRadius layer properties.
Solution
@IBDesignable
class UberView: UIView {
@IBInspectable var cornerRadius: CGFloat = 0 {
didSet {
layer.cornerRadius = cornerRadius
}
}
Views 85
iOS Spellbook
layer.borderColor = borderColor.CGColor
}
}
}
4. Robot
Use UberView to design a robot.
Here's mine:
Views 86
iOS Spellbook
In case you didn't already know, there are thousands of tiny colored
lights/LEDs on the screen that you are using to read this article. Under a
microscope, your screen looks like this:
The computer only knows how much electricity to give to each one of the
three leds that represent a pixel. So if a pixel is red, the green and blue
lights get low or no electricity, and the red one gets a lot.
The RGB color model (stands for Red Green Blue) is an additive color
model in which red, green and blue light are added together in various ways
to reproduce a broad spectrum of colors.
You might remember from school that red, blue and yellow are primary
colors. And that is still true!
When you use paint, light (either from the sun or from another source) hits
the paint and then reflects into your eyes. When you use pixels on a screen,
light is emitted from the screen and then hits your retina :)
To encode a color in RGB you need three numbers between 0 and 255 or
between 0.0 and 1.0 . If you use the floating point representation it will
still get converted to the discrete representation from 0 to 255 where 0
means the light is not turned on for that color and 255 that it's turned on at
the maximum level.
One this to keep in mind is that any display only shows colors in RGB.
The alpha channel describes how much light can pass trough. So if the
opacity is set to 100% the color will get shown on the screen exactly as the
RGB part of the model describes it. If alpha is 0% then the pixel is fully
transparent.
The inventors named alpha after the Greek letter in the classic linear
interpolation formula (1 - α) * A + α * B .
Making all values 1 will give white. And if you make the rgb part 0 and
alpha 1 you get black.
lerp stands for linear interpolation and is the usual name this function is
given. If you ever saw some code with a lot of lerping, now you know what
that meant :)
If you want to learn more about linear interpolation, you can try the lessons
from Khan Academy or if you are lazy like me you can watch this amazing
talk by Steven Wittens - he has more cool videos on his website.
The first thing we need to do is get the color components back from a
UIColor . Unfortunately there are no red , green , blue , alpha
properties:
extension UIColor {
func components() ->
(red:CGFloat, green:CGFloat,
blue:CGFloat, alpha:CGFloat) {
var r: CGFloat = 0
var g: CGFloat = 0
var b: CGFloat = 0
var a: CGFloat = 0
return (r, g, b, a)
}
}
extension UIColor {
...
return color
}
}
Before After
Complementary Colors
Complementary colors are pairs of colors which, when combined, cancel
each other out. This means that when combined, they produce a grey-scale
color like white or black. When placed next to each other, they create the
strongest contrast for those particular two colors.
Contrast for colors means how much the color are different. To get the
complementary of a color you need to reverse the intensity of each color
channel:
extension UIColor {
var complementary: UIColor {
let (r, g, b, a) = components()
return UIColor(red: 1 - r,
green: 1 - g,
blue: 1 - b,
alpha: a)
}
}
from left to right colors vary on hue and from top to bottom on
brightness
Stop using it in projects that other people will see. Use a set of colors you
find online. There are thousands of sites providing free collections of pretty
colors.
Zen bits
extension UIColor {
class var myGreen: UIColor {
return UIColor(red: 0.15, green: 0.68, blue: 0.38, alpha: 1)
}
...
}
Conclusion
It's easy and fun to create and combine colors, and with a bit of common
Exercises
Color Gradient
func makeGradient() {
let width = Int(view.frame.width)
for i in 0...width {
let frame = CGRect(x: CGFloat(i),
y: 0,
width: 1,
height: view.frame.height)
line.backgroundColor =
UIColor.myRed.combine(with: .myBlue,
amount: t)
view.addSubview(line)
}
}
More Colors!
Add light , lighter , darker and dark versions of the colors from
Colors.swift :
extension UIColor {
func light() -> UIColor {
return combine(with: .white, amount: 0.2)
}
extension UIColor {
class var myLightGreen: UIColor {
return myGreen.light()
}
return myBlue.light()
}
func showShades() {
let width: CGFloat = 20
let spacing: CGFloat = 5
line.backgroundColor = color
view.addSubview(line)
}
}
Animations
While changing view properties at runtime is useful, it doesn't look pretty.
Most changes look better if they are animated from one state to another.
UIView provides a very simple API for that. Do whatever changes you want
to do to the view, but in a closure and set a duration. All changes on any
animable property will be beautifully animated.
frame
bounds
center
alpha
backgroundColor
Animations 102
iOS Spellbook
transform
contentStretch
UIView.animate(withDuration: 3) {
redView.backgroundColor = .blue
redView.frame = newFrame
}
Customizing animations
The UIView animation API provides 4 methods:
animate(withDuration:animations:)
animate(withDuration:animations:completion:)
Animates the changes to one or more views over a period of time after
which it calls the completion handler.
animate(withDuration:delay:options:animations:completion:)
Animates the changes to one or more views over a period of time using
a set of options after which it calls the completion handler.
animate(withDuration:delay:dampingRatio:velocity:options:animation
s:completion:)
Animations 103
iOS Spellbook
UIViewAnimationOptions
.allowUserInteraction
By default user interaction is turned off for views while they are animated. In
case you want to animate a view and still have user interaction enabled use
this option.
.repeat
.autoreverse
Performs the animation forward one and then backwards. If you animate a
change to the backgroundColor of a view, you will see the animation going
from the original color to the new one and then animating back to the
original color, then jumping to the new color. Because of this the
.autoreverse option should be used combined with the .repeat option.
.beginFromCurrentState
If this key is not present, any animations affecting the current view(s) are
allowed to finish before the new animation is started. If there are no
animations on the current view(s) this key has no effect.
Animations 104
iOS Spellbook
.curveEaseIn
Will cause the animation to begin slowly and then speed up in the end
.curveEaseOut
Will cause the animation to begin quickly and then slow down in the end
.curveEaseInOut
Will cause the animation to begin slowly and then speed up until the middle
and then slow down in the end
Practice Time!
Download the starter project.
Starter Project
Animations 105
iOS Spellbook
Solution
setSquare(color: .myBlue) {
self.setSquare(alpha: 0) {
Animations 106
iOS Spellbook
self.setSquare(alpha: 1) {
self.setSeqare(size: doubleSize) {
self.setSeqare(size: originalSize) {
self.rotateSquare() }}}}}
}
func rotateSquare() {
UIView.animate(withDuration: 0.4,
delay: 0,
options: [.repeat],
animations: {
self.square.transform =
CGAffineTransform(rotationAngle:CGFloat(M_PI_2))
}, completion: nil)
}
}
Animations 107
iOS Spellbook
Tap! Swipe. Zoom. Scroll. Touch is at the heart of iOS. In this chapter we
are going to take a brief overview at the how touch events are handled by
iOS and how to detect gestures.
Touch interfaces are so intuitive you don't really need to learn that much to
use them. My son is an one year old internet user. He picks his own
cartoons or songs.
UIResponder
Because all you see are UIViews , it was an easy choice to let them handle
touch events. You can respond to user interaction in three main ways: 1) by
overriding UIResponder methods
2) by attaching gesture recognizers to a view
3) by taking a hitTest
UIView inherits from UIResponder , which is the base class that defines a
responder object. A responder is an object that responds to user interaction
events. UIApplication also inherits from UIResponder .
This is how a responder object looks like when it's doing his job.
There are two general kinds of events: touch events and motion events. For
now we are going to focus on the first ones.
Let's see what happens when you put your finger on the screen!
After all the hardware magic, UIApplication gets event objects, it passes
them to its UIWindow , who then sends them to the view that has been
touched and down the chain of responders.
That view is found by going over the view hierarchy and performing a hit
test. UIView has a method called point(inside:with:) - the first
parameter is a point and the second one is a touch event - the function
should return true if the point is inside its bounds.
UIWindow calls the hitTest method on the view of the current view
controller. It performs the hit test recursively on all subviews. The hit test
returns the farthest view in the hierarchy that passed the hit test. To hit test
a view you need to call the point(inside:with:) method with the current
touch location and event. If that returns true , the view passed the hit test.
The hit test for a view will prefer to return a subview who passed the test
instead of itself. So if you have a container view that has your the rest of
your UI and controls, the events will get to them instead of the container.
Quick tip
If you need to make an irregular shaped button you are going to need
to override point(inside:with:) . An if you represent the button
shape by a bezier path you can use CGPathContainsPoint(_:) to
detect if the touch was inside of the path or not (source).
The first one is UIApplication . The one you care the most about is
UIViewController , your view controller.
For touch events, UIResponder has four main methods that get called so it
can react to them:
touchesBegan(_:withEvent:)
touchesMoved(_:withEvent:)
touchesEnded(_:withEvent:)
touchesCancelled(_:withEvent:)
Mini Project
Enough theory. Let's have some fun with UIResponder !
We are going to make a small app that makes a view appears when you
touch the screen.
Create a new Xcode project. Name it Touches and make sure the language
is set to Swift .
...
}
}
Every time you tap on the screen that method gets called. It receives two
parameters: a set of touches and an UIEvent that contains the set of
touches and more information about the event.
For each finger touching the screen, iOS generates a UITouch object. For
most cases you only have one finger touching the screen. Let's make the
app print the location of the tap.
First we need to get a touch object from touches and after that we can call
the location(in:) method on it to get a CGPoint that represent the
location of the touch relative to a view. We are going to pass the view
controller's view view :
...
Run the app and give it a few taps. The console should open and print the
location of each tap:
Now add a black circle with the center in the touch location:
...
Run the app. After a few taps it should look like this:
When you move your finger, the view doesn't follow your finger. Let's solve
that. First thing you need to do is to keep a reference to touchView in the
viewController.
...
}
...
Override touchesMoved - as a start let's copy paste the code from above.
That will create a new circle when the touch moves:
Now the app behaves like a crude drawing app. This is not a practical
example because it will shortly add hundreds of views to the screen and
that will make the interface slow (and finally it will break). Also, it doesn't
handle the case when you move your finger fast on the screen, adding only
Remove the copied code after you get location from the touch event,
except for the line that sets the center of touchView :
...
touchView.center = location
}
}
}
If you run the project now, the black circle will follow you finger.
First thing you need to do for that is to enable multi-touch on view . Open
Main.storyboard and select the view of the view controller:
Now if you touch the screen with multiple finger you will receive more than
one even in touches .
If you look at the definition of Set , you will notice that it only accepts
Hashable elements:
That means you can get the hashValue of a touch and use it as a key in a
Dictionary that will hold the touch views.
So remove the touchView property and add a new one called touchViews :
...
}
Now that we have more than one touch instead of getting the first from the
set we are going to iterate over the set. Good news, only two lines of code
will change in each UIResponder method.
Lets start with touchesBegan . The first one will be replace with a for in
statement:
The next thing we have to change is how we create and store the touch
view.
touchView.layer.cornerRadius = touchViewFrame.width / 2
touchView.center = location
touchView.backgroundColor = .black
view.addSubview(touchView)
touchViews[hash] = touchView
}
}
}
Comment out the code from the other methods and try it out:
To make them update we need to change the way we get the touch view:
Although you probably won't use these methods to often, or ever, it's
important that you at least know that they exist.
Gesture Recognizers
Handling more complicated interactions just by observing the different touch
locations seems almost impossible, thankfully this is already implemented
by Apple and available in UIKit . As you've guessed, I'm talking about
Gesture Recognizers.
Tap - can detect taps - you can customise the number of taps or fingers
needed to activate the gesture
Pan - pan simply means moving across the view
Swipe - similar to the pan gesture, except a swipe is a swift gesture.
Pinch - this is the gesture used to zoom in/out
Long Press - detects a long press - you can customize the minimum
duration and the number of fingers required
Rotation - detects a rotation - this is a gesture with two fingers - you
can extract the angle and speed of the gesture
Screen Edge - detects a swipe from the edge of the screen
Starter Project
Click on it and open the Attributes Inspector . There you will see the
customisation points of the tap gesture recognizer:
The most used customization point is the minimum number of taps and
touches require to activate it. A double tap gesture recognizer is a tap
gesture recognizer with numberOfTapsRequired property set to 2 .
Another thing you can do is to disable the gesture recognizer and enable it
programatically later in your code.
The gesture recognizer is all setup at this point. The only problem is that
you don't get notified when a tap occurred. Open the Connections
Inspector :
This generates a method that will be called after a each tap event:
Zen bits
The process is pretty much the same for all gesture recognizers. But I didn't
mention a thing about gesture recognizers so far; they have a state .
Some gestures are more complicated than a tap. Let's take for example the
pan gesture: when you put a finger on the screen the gesture is not
activated, but still possible, when you move your finger a bit the gesture is
recognized or it started, when you move it a bit more the gesture changes
its location and after you take you finger off the screen the gesture ended. If
you followed that you can almost guess the main states of a gesture
recognizer:
The selector you set in Interface Builder will get called every time the
gesture recognizer state changes. That mans that, for example, if you use a
swipe gesture recognizer you will only want to handle the case where the
state is .ended . And for a tap gesture recognizer, a single touch event is
enough to set the state to .ended , so that why you only get one call to the
didTap(_:) method.
Lets continue expliring different recognizers! Add a referencing outlet for the
red view:
...
}
Search for Pan Gesture Recognizer in the Components Library and drag
one on redView .
Run the app. You should see multiple did pan.. message when moving
over the view.
Now for a bit of magic. Lets translate the view to follow you finger:
redView.frame.origin.y += translation.y
And now to handle scaling the view with the help of a pinch gesture
recognizer:
// reset scale
sender.scale = 1.0
}
}
If you want to test this in the simulator you will need to hold Option ( ⌥ ) to
show two cursors:
If you move your mouse the points will move symmetrical to the point
between them. It's a bit hard to control but you get used to it. So
Option ( ⌥ ) + moving is a pinch or rotate gesture.
If you want to move both points to another part of the screen you need to
hold both Option ( ⌥ ) and Shift ( ⇧ ) and then move the cursor on the
screen. Don't let go of Option ( ⌥ ) because that will reset the position.
Let's continue our project by adding the rotation gesture recognizer. When
doing this in code, the steps are a bit reversed. First you need to create the
callback:
attach it to redView :
let rotationGestureRecognizer =
UIRotationGestureRecognizer(target: self,
action: #selector(didRotate(_:)))
redView.addGestureRecognizer(rotationGestureRecognizer)
}
}
Run the project. See if you can trigger the print call.
Let's change the color of the view when we make a rotation gesture:
redView.backgroundColor = color
}
}
Practice Time!
Make a custom view called TouchableView that has the behaviour for move,
scale and change color. Add a touchable view on the screen on a double
tap. Change the shape from a square to a circle on double tap.
Draw something!
Solution
When needed, we are also going to take a short look at the Objective-C
roots of iOS and the system classes/interfaces needed for a specific task.
Start a new project and open the storyboard. Search for the components
mentioned in this chapter and play around with them. For each component
there are a few challenges that should help you practice using and
customising UI controls.
It has all you ever wanted to know about what makes a good app (or a good
user experience). I highly recommend that you read it and refer to it when
taking design decisions with your team. Good app developers also know a
bit about design. Remember that people will judge your app by the way it
looks first and only after on what it can do.
There is a lot to read in the HIG. But for now start with this page
UIComponents: Controls. It shows most of the iOS controls and explains
when and how to use them. If you didn't design an app or website before, I
bet you didn't think like that about labels or buttons.
Zen Bits
UILabel
Labels show small amounts of text, either to name a part of the screen, or to
display information. Almost everywhere you see only few words on a screen
you can bet that it's a label, it may also be included in other controls, for
example: the title of a button is a label.
Code example
Customization
By changing a few properties, you can achieve all kinds of effects:
Note: Because all iOS controls inherit from UIView you can
customise any UIView property on all components.
Font
Fonts are loaded using the UIFont class. Each font has an unique
identifier. To create a font object you need to set the name of the font and
its size:
let hugeLightFont =
UIFont (name: "HelveticaNeue-UltraLight",
size: 30)
Quick tip
You can find a complete visual list of all the system fonts on
iosfonts.com.
label.font = hugeLightFont
System font
System font refers to the default font used for standard interface items. So
when you add a label, its font is System Font - 15 . Since iOS 9, the system
font is San Francisco and before that, it was Helvetica Neue.
Text Color
Similar to the way you can customise the background color by setting the
backgroundColor property, you can change the color of the text by setting
the textColor property:
title.textColor = .white
label.lineBreakMode = .byTruncatingTail
label.lineBreakMode = .byTruncatingMiddle
label.lineBreakMode = .byTruncatingHead
label.lineBreakMode = .byWordWrapping
label.lineBreakMode = .byCharWrapping
label.lineBreakMode = .byClipping
Number of lines
By default labels show 1 line of text. You can make labels display text on
multiple lines, but only do so if you are still using it as a label. Otherwise use
a text view.
label.numberOfLines = 2
If you set numberOfLines to 0 the label will display as many lines as the
text has but not more than it can fit in its size.
Text Alignment
There are five options for text alignment, described by the NSTextAlignment
enum:
.left : Align text along the left edge. This is the default text alignment
option.
label.textAlignment = .left
.center : Align text equally along both sides of the center line.
label.textAlignment = .center
label.textAlignment = .right
.justified : Fully justify the text so that the last line in a paragraph is
natural aligned. This is only used for label that have more than one line
of text.
label.textAlignment = .justified
Attributed Strings
In the example above the label has a title and a subtitle. The title has a
bigger font size than the subtitle. The heart is red and has a font that
doesn't replace the unicode characters with one of Apple's emojis.
First, let's create an attributed string with the title and subtitle in it. To create
an attributed string we need a "normal" one.
label.attributedText = attributedString
Now we can add different attributes for different parts of the string. First of
all, we make the title portion of the text bigger:
...
// title
let titleAttributes = [
NSFontAttributeName: UIFont(name: "HelveticaNeue",
size: 32)!]
attributedString.addAttributes(titleAttributes,
range: titleRange)
let subtitleAttributes = [
NSFontAttributeName :
UIFont(name: "HelveticaNeue", size: 20)!
]
attributedString.addAttributes(subtitleAttributes,
And finally, let's make the heart flat by changing the font to a dingbat and
the color red.
UIButton
Buttons are awesome! They make things happen when you need them to.
The button is also the simplest input you will use. If you had to use only
buttons and labels to make apps, you could still make a lot of them. After
we learn a bit about how buttons work and how to use them, we are going
to make a few simple games to practice.
Target-Action
Selectors
In iOS the target-action pattern usually comes in the combo target-
Swift figures out what function it should call at compile time with a thing call
vtable . Objective-C figures out what function to call at runtime with a thing
called dynamic method dispatch. To do this it needs both an object and a
selector.
A root class is a class that does not inherit from any other class. It defines
the behaviour for the hierarchy below it. In Objective-C the root class is
NSObject - which means that most iOS classes are NSObjects - that
includes views and controllers. NSObject defines a lot of low level
perform(_:)
perform(_:with:afterDelay:)
perform(_:with:afterDelay:inModes:)
...
performSelector(inBackground:with:)
SelectorTest.noParameters
SelectorTest.unnamedParameter(_:)
SelectorTest.namedParameter(message:)
SelectorTest.twoParameters(first:last:)
The first part of the selector is the name of the class followed by a dot .
then by the method name, and if it has parameters, they are shown
separated by a colon : inside of a pair of parentheses. Note that if a
parameter has no external name it will be represented by an _ like in the
second case. In most cases the class of a selector can be inferred, so you
can omit it.
// no parameters
instance.perform(#selector(noParameters))
// prints: no parameters
// two parameters
instance.perform(
#selector(twoParameters(first:last:)),
with: "Andrei",
with: "Puni")
// prints: fullName = Andrei Puni
By using these methods the UIButton class can call any method you tell it,
to just by performing a Selector on a target. This way you can configure
the button to call any method on any object.
Usually you connect buttons to the view controller that displays them, but
that is not the only case. In code it looks something like this:
// setup is called by the view controller sometime after the interface loads
fun setup() {
// first we need to add a target to the button
button.addTarget(controller,
action: #selector(didTapButton(_:)),
forControlEvents: .touchUpInside)
}
There are other button events but you will probably not going to use them.
In case you where curious .touchUpInside refers to the fact that the touch
started and ended inside the bounds of the button.
The selector has to be compatible with Objective-C, that means that the first
parameter should be unnamed.
IBAction
...
If you remember from the App Anatomy chapter, we discussed about the
Objective-C preprocessor. @IBAction is also a preprocessor directive. It
gets replaced with nothing. Its only purpose is to highlight a method as
available for interface builder connections.
If you right click on a button in Interface Builder you will see a menu with
all the events that it exposes:
The button doesn't actually use all of them, the events are general and
shared among all controls. UIButton inherits from UIControl - the class
that defines the behaviour of an input view or control.
If you right click on View Controller you can see the didTap action in the
bottom of the list:
Now you can connect the didTap method to the Touch Up Inside event.
You can do it from the controller to the button or vice versa:
Right click on the button, then click drag from the Touch Up Inside event to
the View Controller :
Another way you can do it is by starting from the action. Right click on View
Controller and click drag from the didTap: action to the button:
You can also make Xcode write the method for you if you use the
Assistant Editor and connect the Touch Up Inside event with the view
controller code.
Button States
A button can have five states: default, highlighted, focused, selected, and
disabled.
You can configure some properties of the button for each state. This would
allow you to make the font bold or a different color when the button is being
pressed.
When the button first appears on the screen, it is in the default or normal
state
When the user is touching the button but did not yet complete a tap, the
button is in the highlighted state.
You can switch the state of the button to selected by setting the selected
property to true :
button.selected = true
->
When the button is disabled, it's dimmed and doesn't display a highlight
when tapped. You can disable a button by setting its enabled property to
false :
button.enabled = false
Customization
Title
Notice that you have to specify for which state you want the title to be set. If
you want the button to have the same title on all states then set it for the
.Normal state and the button will inherit the title for all states:
button.title(for: .selected)
Tint Color
You can change the tint color by setting the tintColor property:
button.tintColor = .black
If you want to change the tint color for all the buttons from your app this is
not the way to do it! Open the file inspector and look at the end of the
You can change the global tint color from there and all components with the
tint color set to default will have that color.
Image
In iOS images are represented by the UIImage class. You can create an
image from a file (the image for a button) or directly from data (when you
load cat memes from the internet). For the moment we are only going to
load images from the Resources folder, so let's see how that works:
Same as with the title, if you set the image for the .normal state the rest
will inherit it unless you set them to a different value.
Background Image
Same as you can update the image of a button, you can update the
background by calling setBackgroundImage(_:forState:) :
button.setBackgroundImage(image, forState:.normal)
The main difference between the image of the button and its background
image is positioning. If the image is small then it will be shown along the
title. The background will always cover the button and will be rendered
under the title.
System buttons
At the moment there are two system buttons you can use. There is nothing
different about them, except for their appearance.
Add Contact
Info
Used to display extra information. There are two types of info buttons: one
for a light background and one for a dark background.
From what I've noticed the two look the same. Weird.
UITextField
A text filed is a label which can be edited. It can be configured to use
different kinds of keyboards optimised for writing numbers, emails and so
on.
Editing events
Whenever you interact with a text filed it generates an event, so the UI can
react. There are two ways to receive editing events: you can subscribe
using the target-action pattern or by providing a delegate to the text filed.
Target Action
The target action should take as argument the control that was sent by the
event, in this case the text field.
One of the problems with this method is that you can't customize the
behaviour of the text by rejecting certain characters or deciding if editing
should stop or not.
In the case of the text field delegate, it receives editing events and
sometimes its asked to decide about the text view behaviour.
All the methods of UITextFieldDelegate are optional so you can use only
the ones you need.
Some of the methods from the protocol just inform the delegate that a
certain event has occurred:
textFieldDidBeginEditing(_:)
textFieldDidEndEditing(_:)
And some ask the delegate if a certain action should be taken or not by
retuning a Bool :
textFieldShouldBeginEditing(_:)
textFieldShouldEndEditing(_:)
textFieldShouldClear(_:)
textFieldShouldReturn(_:)
textField(_:shouldChangeCharactersIn:replacementString:)
Notice that all event related methods have the form sender + did +
event and all the rest have sender + should + action . This convention
keeps the system code easy to understand or discover.
Quick tip
You don't need to know the list of methods from the delegate protocols
of any control! It's enough to remember that they have one. They
usually have the name of the control + delegate so its easy to deduce.
Write it in Xcode and Command ( ⌘ ) + click on it. That will take you to
the definition of the protocol.
let result =
text.replacingCharacters(in: range,
with: string)
Keyboard types
There are several system keyboard layouts optimised for different kinds of
input:
Default
The default keyboard is the general text input. Depending on the language
of the phone, the keyboard might change. For example if the language is
set to Russian, the keyboard would look like this:
ASCII Keyboard
textField.keyboardType = .asciiCapable
You can access this keyboard by pressing the bottom left button on
system's keyboard optimised for words.
textField.keyboardType = .numbersAndPunctuation
URL
A keyboard optimised for entering URL. It's similar to the default keyboard
but it has the space key replaced with . , / and .com .
textField.keyboardType = .URL
Similar to the URL keyboard this one replaces the space key with @ and
. . And also leaves a small space key, so the user is able to enter multiple
emails.
textField.keyboardType = .emailAddress
Phone numbers
A keyboard that mimics the keyboard of what people used to call phones.
This keyboard is used by the contacts app.
textField.keyboardType = .phonePad
Web search
The web search keyboard adds a . in the bottom right corner next to
space and replaces the return button with Go
textField.becomeFirstResponder()
To dismiss the keyboard you have to tell the text field to resign as the first
responder:
textField.resignFirstResponder()
In case you don't have a reference to the textField, you can tell the view that
contains the text field - or another input - to end editing and dismiss the
keyboard:
view.endEditing(true)
This is really convenient when you have may inputs on the screen because
you don't have to change your code if you change the interface.
The text field is the most used component for text input. It is also the most
misused component! There are a lot of cases where text input is not
necessary because the number of valid inputs is small or you can reduce
them without losing functionality or there is a better control for that kind of
input (ex. RGB values).
Let's take for example apartment listings. In a search form there is usually a
field for the number of rooms:
Before you throw a text field in front of the user, ask yourself if you can
replace it with another control that will do the job better that entering text.
Customization
Placeholder
When the text file is empty it displays a placeholder string to signal what
should be written inside it. When the text field becomes the first responder,
the placeholder disappears.
Border
Depending on the style of your app you might want to use a different kind of
border than the default one:
Rounded
Displays a rounded-style border for the text field. This is the default option.
textField.borderStyle = .roundedRect
Line
textField.borderStyle = .line
Bezel
Displays a bezel-style border for the text field. This style is typically used for
standard data-entry fields.
textField.borderStyle = .bezel
None
textField.borderStyle = .none
Background Image
You can change the way a text field looks by changing it background image.
The image will be stretched to fit the size of the text field.
Secure Input
For passwords or other sensitive information you should user secure text
textField.secureTextEntry = true
UITextView
The text view is a scrollable multi-line text container that also supports
editing. The purpose of the text view is to display large amounts of text like
comments of the body of an email.
Links
A cool thing that you can do with a text view and can't with a label or text
field, is to let the user open links.
By default iOS will give safari any website. Some links have a different
format and will be handled by an app that supports it. For example opening
tel://0800123123 will call 0800123123 and sms://0800123123 will open the
Messages app in the new message screen. If you want to see a complete
list of URL schemas, browse this page.
Delegate
Another thing the delegate lets you customize is the behaviour of your app
when the users opens an URL. Instead of opening the application
responsible for that URL, you can load the content in a web view or trigger
an alternative action.
openWebViewAt(URL)
return false
}
}
Handling Keyboard
The keyboard is controlled the same way as the text field is. Make the text
view the first responder to show the keyboard and resign it to dismiss the
keyboard.
Customization
Text Alignment
You can change the text alignment by setting the textAlignment property.
Left
textView.textAlignment = .left
Center
Text is centered.
textView.textAlignment = .center
Right
textView.textAlignment = .right
Justified
textView.textAlignment = .justified
Natural
The default alignment associated with the current localization of the app.
textView.textAlignment = .natural
Scrolling
By default a text view is scrollable. You can disable scrolling by setting the
scrollEnabled property to false :
textView.scrollEnabled = false
Editing
You can enable or disable the editing behaviour of the text view by setting
the editing property.
textView.editable = false
Attributed Text
Like the label, the text view can display attributed text.
UIImageView
UIImageView is the view that displays a single UIImage . The image can be
set using the image property.
By default UIImageView will change its size to fit the image unless you set
its size.
Open Assets.xcassets :
Now open finder and go to the folder that contains the resources you want
to add:
Click and hold the resource and start dragging it in the direction where
AppIcon used to be in Xcode. Don't let go of the file! Hit
Command + tab ( ⌘ + tab ) to switch back to Xcode and drop the file
somewhere under the icon image set.
An image set is a collection of images that are the same image at different
resolutions. This comes from the fact that iOS devices have retina screens
with different pixel densities.
To specify the pixel density, you have to add the suffix @2x for 2 by 2 pixels
per point and @3x for 3 by 3 pixels per point. You can find the complete list
of specification for naming your resources here.
Select the image set you want to add more resources to, after that go in
finder and drag and drop the resources inside it:
Content Mode
The content mode of an image specifies how the content should be drawn.
scaleToFill
The default value for contentMode is .scaleToFill which scales the image
so that it fits in the bounds of the image view. Notice that this may distort the
image.
scaleAspectFit
The option to scale the content to fit the size of the view by maintaining the
aspect ratio. Any remaining area of the view’s bounds is transparent.
scaleAspectFill
The option to scale the content to fill the size of the view. Some portion of
the content may be clipped to fill the view’s bounds.
redraw
The option to redisplay the view when the bounds change by invoking the
setNeedsDisplay() method. Also distorts the image.
center
The option to center the content in the view’s bounds, keeping the
proportions the same.
top
The option to center the content aligned at the top in the view’s bounds.
bottom
The option to center the content aligned at the bottom in the view’s bounds.
left
The option to align the content on the left of the view.
right
topLeft
The option to align the content in the top-left corner of the view.
topRight
The option to align the content in the top-right corner of the view.
bottomLeft
The option to align the content in the bottom-left corner of the view.
bottomRight
The option to align the content in the bottom-right corner of the view.
User Interaction
UISwitch
The UISwitch is a control that represents a boolean value that can be
flipped on/off. It holds its state inside the isOn property.
Events
You get notified of events by connecting to the valueChanged control event.
Customization
onTintColor
By setting the onTintColor property you can change the color of the active
state:
mySwitch.onTintColor = .purple
thumbTintColor
By setting the onTintColor property you can change the color of the knob:
mySwitch.thumbTintColor = .purple
UISegmentControl
UISegmentedControl is a horizontal control made of multiple segments,
each segment functioning as a button. Each segment can show a title and
an image and has a state, either selected or not. Only one segment can be
selected at once. When a segment is selected, the one that was selected
before (if any) will be unselected. This control is used when the user has to
select between multiple things of the same kind.
Events
@IBAction didSelectSegment(
_ segmentControl: UISegmentControl) {
let index = segmentControl.selectedSegmentIndex
let title = segmentControl.titleForSegment(at: index)
UISlider
The UISlider control represents a value that can range over an interval.
Events
Customization
minimumValue
Use this property to set the value that the leading end of the slider
represents. If you change the value of this property, and the current value of
the slider is below the new minimum, the slider adjusts the value property to
match the new minimum. If you set the minimum value to a value larger
than the maximum, the slider updates the maximum value to equal the
minimum. The default value of this property is 0.0 .
maximumValue
Use this property to set the value that the trailing end of the slider
represents. If you change the value of this property, and the current value of
the slider is above the new maximum, the slider adjusts the value property
to match the new maximum. If you set the maximum value to a value
smaller than the minimum, the slider updates the minimum value to equal
the maximum. The default value of this property is 1.0 .
Thumb Image
You can change the thumb image by using the setThumbImage(_:for:)
method:
Thumb Tint
You can change the thumb tint by setting the thumbTintColor property:
slider.thumbTintColor = .purple
slider.minimumTrackTintColor = .red
slider.maximumTrackTintColor = .black
Exercises
Make an app with a single view controller that displays a round red button.
Set the button title to Tap Me! .
Make the button move to a random position every 2 seconds. You can do
this with the help of perform(_:with:afterDelay:) .
Solution
Color Tap
Make an app with a single view controller that displays 6 colored buttons
and two labels. One of the labels is the score label and the other one is the
challengeLabel.
Every round the label is set a random color and text. The text is the name of
a random color. The user scores 1 point if he touches the button with the
color the text says and -10 otherwise. If the user does not select a color in
Make the phone vibrate every time the user makes a wrong move:
AudioServicesPlayAlertSound(
SystemSoundID(kSystemSoundID_Vibrate))
You will need to import the the AVFoundation framework. Click on the
project, then on target, then select the Build Phases tab. In the Link Binary
With Framework category press the "+" button and search for AVFoundation.
Solution
Tweet Screen
Make a tweet screen usint UITextView . A label should indicate the number
of characters they have left until 140. The label should have the text color
gray if the number of empty spaces is above 10 , green is it's under 10
and above equal to 0 , and red if there are more than 140 characters in the
tweet.
Solution
Impossible Game
Make an app with a single screen that has two switches and a label with the
message "Turn both switches on to win!". Initially both switches are off.
When both switches are on, flip the switch that was changed before.
Solution
Alpha slider
Bind the value of a slider to the alpha value of a view.
Solution
Color Picker
Make a color picker that also show lighter and darker shades so you can
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
PlaygroundPage.current.liveView = colorPicker.view
⌥ + ↩ ). You should be able to see a white rectangle in the right side of the
playground window.
Step 4: Basic UI
func loadView() {
...
let sliderSize =
CGSize(width: width - 2 * padding,
height: 30)
let redSliderFrame =
CGRect(origin: CGPoint(x: padding, y: 300),
size: sliderSize)
view.addSubview(redSlider)
let greenSliderFrame =
CGRect(origin: CGPoint(x: padding, y: 340),
size: sliderSize)
view.addSubview(greenSlider)
let blueSliderFrame =
CGRect(origin: CGPoint(x: padding, y: 380),
size: sliderSize)
view.addSubview(blueSlider)
}
}
}
Note that all sliders call the didMoveSlider(_:) method when their value
changes. That's because we have to do the same thing if any of them
changes - update the color on the screen.
func loadView() {
...
let colorViewFrame =
CGRect(x: padding, y: padding,
width: width - 2 * padding,
height: width - 2 * padding)
colorView = UIView(frame: colorViewFrame)
colorView.backgroundColor = .white
colorView.addBorder()
}
}
The last line ads a border around the view so we can see the view even if
it's white.
extension UIView {
func addBorder() {
layer.borderWidth = 0.5
}
}
func updateColor() {
let redAmount = CGFloat(redSlider.value)
let greenAmount = CGFloat(greenSlider.value)
let blueAmount = CGFloat(blueSlider.value)
colorView.backgroundColor = color
}
let shadesCount = 10
for i in 0..<shadesCount {
// light shade
let lightShadeFrame =
CGRect(x: padding + CGFloat(i) * shadeSize,
y: 430,
width: shadeSize,
height: shadeSize)
let lightShade = UIView(frame: lightShadeFrame)
lightShade.backgroundColor = .white
lightShade.addBorder()
lightShades.append(lightShade)
view.addSubview(lightShade)
// dark shade
let darkShadeFrame =
CGRect(x: padding + CGFloat(i) * shadeSize,
y: 430 + shadeSize,
width: shadeSize,
height: shadeSize)
let darkShade = UIView(frame: darkShadeFrame)
darkShade.backgroundColor = .white
darkShade.addBorder()
darkShades.append(darkShade)
view.addSubview(darkShade)
}
}
}
func updateColor() {
let redAmount = CGFloat(redSlider.value)
let greenAmount = CGFloat(greenSlider.value)
let blueAmount = CGFloat(blueSlider.value)
colorView.backgroundColor = color
for i in 0..<shadesCount {
let t = CGFloat(i) / CGFloat(shadesCount)
Set the value of each slider to 0.5 so we can test if the shades are
displayed correctly:
redSlider.value = 0.5
greenSlider.value = 0.5
blueSlider.value = 0.5
updateColor()
}
}
Set the background color of the ColorPicker to a really white version of the
selected color:
func updateColor() {
...
view.backgroundColor =
color.combine(with: .white, amount: 0.9)
}
}
Auto Layout
A few years ago you might have gotten away without Auto Layout. Now
there are a couple of different iPhone sizes and on iPad you can run two
apps at the same time. Auto Layout is the way to go!
This chapter will cover the basics of Auto Layout so that you can start using
it in your apps. We are going to go in depth on a few examples so that we
cover all the basic types of relations you can have. In the end there are a
couple of exercises to practice what you've learned.
Auto Layout is a system that makes it easy to support multiple screen sizes
with one interface, by making your interface react to changes. It does this
by solving a set of layout constraints which describe the interface.
vs:
Another reason might be that it is the default on iOS. With only a few
exceptions, Auto Layout is the right tool for the job.
Layout constraints
Layout constraints are descriptions of the mathematical properties and
relations between views. The NSLayoutConstraint class is used to create
constraints on both iOS and Mac. All constraints have a coefficient and a
constant.
In the end by solving the given set of constraints, Auto Layout will
determine the frame for each view in your screen. So each view should
have constraints that will help determine its width , height , x and y
position. There are a few exceptions but we are going to get into them later.
Relations to parent
A view can change its size and location based on its superview. But that
does not work the other way around.
One of the most common things you will do is making a view fill its parent.
Starter Project
Find the initial view controller and add a view from the Object Library .
Resize the view so it fills the screen. You should see some helper lines
when you have the correct size.
Ok that how it looks on an iPhone 7... how will this interface look like on
other devices?
Xcode has a tool for this! Open the Assistant Editor from the segmented
control in the top right corner of Xcode.
The default will open the swift code of the view controller you where editing.
In the top of the Assistant Editor you will find this menu. Click on
Automatic.
You can add more devices from the + button in the lower left corner.
Now we see that our interface has a few problems in landscape mode and
on iPad.
If you feel like you need more space when working on your interface with
Preview opened, you can close the Navigator from the control on the top
OK. Now that you have enough room, let's start adding some constraints.
Hold the control key ⌃ and then click drag from the red view to its
superview.
Hold alt ⌥ to change the available constraint options. All the constraints are
the same. The only thing that differs is whether they are relative to the
margin or not. We are going to cover margins later in this tutorial.
Quick tip
You can inspect the constraints you added from the Size Inspector .
It's the space between the view and its superview. Leading is on the left.
Trailing is on the right. In the example we did before, these were all set to
0 .
How can I change that? What if I want to leave some empty space?
Select the leading space constraint. You can find it by selecting the view
and going to the Size Inspector . Click on the Edit button and set the
constant to 40 . You will notice that the view will resize according to the
new constraint. This only happens when you edit a constraint. When you
change the frame of the view you will get a layout warning or error.
Can you guess what relation the Leading Space constraint describes?
View.Leading = Superview.Leading + 40
One thing we need to understand is that the constant will change its sign
when we reverse the first and second item in order to maintain the same
relation.
Superview.Leading = View.Leading - 40
In the storyboard there should be another screen. Make that one the initial
view controller.
Inspector .
Our first goal is to make the BottomContainer stick to the bottom of the
screen and fill up the width of the screen. TitleLabel should fit inside.
Making the container stick to the bottom and fill up the width of the screen is
pretty similar to making it fill the screen. Instead of adding a Top Space
constraint we are going to add one for the height of the container.
First add the constraints for leading, trailing and bottom space. Remember
to hold alt ⌥ to change the options.
Select BottomContainer and click on the Pin button. Check Height and
then add the constraint.
If we look at the preview now we see that we are getting closer to our goal.
Inequalities
Let's make the TitleLabel fit inside the container. Add constraints for
leading, trailing, and top space from TitleLabel to its container:
You only need 3 constraint because the label gets its height from the font
size.
The label fits inside the container. But there is not enough room for the text
on smaller screens. We don't want all that empty space. Instead we want
the trailing space between the label and container to be at least 20 points.
Constrains don't only express equality relations. You can also say that a
certain attribute should be greater than or less than another one.
Item and Second Item reversed. In that case set the relation to Less Than
or Equal and the constant to -20 or reverse the order from the
First/Second Item dropdown.
dropdown shown when clicking on the First Item. The last option
swaps items.
If you look at your constraint now you see that it shows a ≥ sign to indicate
Relations to sibling
You can see in preview that the heart will show in the center of the screen
on all devices. Click on the Update Frames button from the bottom bar if
you see a yellow indicator.
We want to make the We and Swift labels to be on the same level and
height as the heart, with a bit of spacing in between. We can do that! With
Auto Layout you can add constraints between views that have a common
superview (directly or indirectly).
Move the labels so that they are at the same level with the heart. Control
drag from each label to the heart and add a horizontal spacing constraint to
set the spacing between the label and the heart.
You will also need an align center vertically constraint to make the label be
on the same level with the heart. Add an equal height constraint to maintain
proportions.
Now the labels will be on the same level with the heart.
Aspect Ratio
The heart logo a bit too small on an iPad, right?
What's that?
Control-drag from the heart to the screen and select the Aspect Ratio
Make sure you have Heart.width as the first item and Superview.width as
the second one. Depending on the direction you dragged you might have
gotten different combinations of width and height.
You can see that the constant for this relation is 0 and the coefficient is
60:667 or about eleven times smaller . Interface Builder lets you
express ratios in a more human friendly way - fractions! Because when you
see 0.09375 you don't immediately think - "Oh! That's 30/320 ". Fractions
are way more intuitive.
The gray background was added to make the aspect ratio clear
As you can see, this didn't make the heart bigger - it only made it wider. To
make it grow we need another constraint. This time from the width of the
heart to its height with a ratio of 1:1 - which will make the height of the
heart equal to its width, like a square. Control-drag from the heart to itself
and select the Aspect Ratio constraint.
The gray background was added to make the aspect ratio clear
To make the text be proportional with the heart, we need to add two
constraints for each label. One from the label to itself, so it will maintain its
aspect ratio. And another one to the heart to fix the height.
One last thing to notice here. On the iPad the text won't grow but the size of
the label will. You can fix this by setting the font size to something bigger -
like 100 .
Stack View
So far so good. We made the We ❤ Swift logo with an image and two
labels. This approach has a small problem, you can clearly see on small
devices that the logo, as a whole, is not actually centred inside the view.
One way would be to move the components into a container and center that
container. Starting with iOS 9 we have a special container that makes this
kind of layout easy to implement - UIStackView. UIStackView uses Auto
Layout to arrange its subviews, one after the other, horizontally or vertically.
You can customise the spacing, alignment and distribution of subviews.
The alignment style refers to the way the stack view arranges its subviews
perpendicular to its axis. Here are a couple of examples of alignment:
Top
Leading
Center
Bottom
Trailing
Let's make the main menu for a game using a stack view!
Add a few buttons for the different options from the menu and select all of
them.
Find the Stack button in the lower right corner of Interface Builder and
press it.
By default the spacing is 0 and the alignment is fill. Change the spacing to
10 and the alignment to Center .
Remove the align center constraints from the heart and the horizontal
spacing ones between the heart and each label. You can do this by
selecting the constraint and the hitting the delete key.
Select the labels and the heart at the same time and then click on the
Stack button.
Select the stack view and set the alignment to Center and spacing to 10
points.
Add the align center constraints to the stack view. And one more constraint
to make the width of the stack view less than the width of the screen.
One more constraint between the stack view and the view for aspect ratio
and we are done:
Intrinsic size
You noticed that when you change the font size of a label, the size of the
label changes. The same happens when you change the text it contains.
You guessed it. The label uses some math magic to determine its size and
then tells Auto Layout how big it is.
To add support for this on your own view, all you need to do is to override
the intrinsicContentSize() method. If you do not have an intrinsic size for
any dimension you can return UIViewNoIntrinsicMetric . The default
implementation of intrinsicContentSize() returns
UIViewNoIntrinsicMetric for all dimensions. Some components, like the
UISwitch , have an intrinsic size for both dimensions, other just for one -
like a text field - the height is fixed but the width isn't.
In case your content changes, you can notify Auto Layout to update your
view by calling invalidateIntrinsicContentSize() on it.
Margins
Sometimes you want to add interface component close to the side of a
container, but not starting from the edge. The size of that space will depend
on the screen size. On an iPad you will want to leave more space than on
an iPhone. Fortunately iOS has a standard margin size defined for each
device. To arrange a view relative to the margin of its container, all you
need to do is make the constraint relative to the margin.
The default margin on iOS is {top: 8, left: 16, bottom: 8, right: 16}
By using them you can adjust the placement of multiple views inside your
container.
I'm going to add the same constraints from code in the three different ways.
The result will look like this:
NSLayoutConstraint
let leadingConstraint =
NSLayoutConstraint(item: self.redView,
attribute: .leading,
relatedBy: .equal,
toItem: self.view,
attribute: .leading,
multiplier: 1,
constant: 50)
let trailingConstraint =
NSLayoutConstraint(item: self.redView,
attribute: .trailing,
relatedBy: .equal,
toItem: self.view,
attribute: .trailing,
multiplier: 1,
constant: -50)
let topConstraint =
NSLayoutConstraint(item: self.redView,
attribute: .top,
relatedBy: .equal,
toItem: self.view,
attribute: .top,
multiplier: 1,
constant: 50)
let bottomConstraint =
NSLayoutConstraint(item: self.redView,
attribute: .bottom,
relatedBy: .equal,
toItem: self.view,
attribute: .bottom,
multiplier: 1,
constant: -50)
let constraints = [
leadingConstraint,
trailingConstraint,
topConstraint,
bottomConstraint
]
self.view.addConstraints(constraints)
Visual Format
let bindings =
Dictionary(dictionaryLiteral: ("redView", self.redView))
let horizontalConstraints =
NSLayoutConstraint.constraints(withVisualFormat:
"H:|-50-[redView]-50-|",
options: [],
metrics: nil,
views: bindings)
self.view.addConstraints(horizontalConstraints)
let verticalConstraints =
NSLayoutConstraint.constraints(withVisualFormat:
"V:|-50-[redView]-50-|",
options: [],
metrics: nil,
views: bindings)
self.view.addConstraints(verticalConstraints)
Cartography
layout(redView) { view in
view.edges == inset(view.superview!.edges, 50, 50, 50, 50)
}
You can read more about Cartography and how to use it on github.
To change the frame of a view that has constraints, you can change the
constant or multiplier of any constraint then call layoutIfNeeded() on
that view. If you click on a layout constraint in Interface Builder you can
connect a referencing outlet from the Connections Inspector . See the
example below:
...
func makeProfilePictureTaller() {
UIView.animate(withDuration: 0.4) { {
// change constraint
self.profilePictureHeightConstraint.constant = 100
// this will change the frame
self.profilePicture.layoutIfNeeded()
}
}
}
UIView.animate(withDuration: 0.4) {
self.article.removeConstraint(articleHeightConstraint)
self.view.addConstraint(articleEqualHeightConstraint)
self.article.layoutIfNeeded()
}
Useful Shortcuts
control-drag from any view to add a constraint on it. After you click, a menu
will appear. If you press Option ( ⌥ ), the options will change - making them
relative or not to the margin.
⌃ + ⇧ + click : shows you a list of all the objects that are under the
cursor in Interface Builder .
Conclusion
Auto Layout is a powerful tool - and the default layout engine for iOS.
Although, most cases can be solved directly from Interface Builder. It's a
good idea to understand what happens under the hood.
Exercises
All exercises will give you a preview of an interface on a couple of devices.
Your task will be to recreate them based on the instructions.
1) The red view should have a width equal to 2/3 of the width of the screen
and half the height. The blue view should have a 1/3 of the width of the
screen. And the purple view should have half the height and the same width
as the screen.
2) This is the view for a random cute image viewer. It has an image view
that fills most of the screen and three buttons.
you can find the kung fu squirrel in the starter project in the asset
catalog
UIControl (button, slider, text field, switch) can send events to the
Controller that displays it.
Views and Models can exchange messages indirectly by talking
with Controllers. For example when a button has been tapped the
controller is usually notified about the event.
UIViewController
You may have noticed that the class for a controller in iOS is
UIViewController and not UIController . Why? Well, that's because
controllers in iOS are a specific kind of controller, one that has a
corresponding view which it handles. You can access the view by using the
view property. That view represents what the controller will display.
That means that a lot of the hard work is already done and can be used on
all view controllers. To harness this power you will need to understand a
few things:
View controllers are similar in the sense that they have these three stages,
but each one has some specific things to look out for.
The general story of the view controller goes something like this:
somewhere in an app, our hero, the view controller, gets created by another
object. At this point the view controller has no view. The creator shows him
on the screen and a view gets created for our view controller. After a while,
another view controller might be presented on the screen, blocking the view
of our view controller - something like a camera screen. That screen is
dismissed and our view controller is visible again. After a (short) while our
hero gets removed from the screen and dies. The end!
LoadView
When you show a view controller, the first thing that it will do is to create its
view, if it didn't before. There are three ways of doing this:
The thing you need to remember here is that all options in the end call the
loadView method. You should never call loadView in your code directly.
The controller automatically calls loadView the first time its view property
is called and its value is nil .
let screenSize =
CGSize(width: width, height: height)
Quick tip
If you want to load the view before it's shown on the screen, you can
simply call the view property.
ViewDidLoad
You've guessed it! viewDidLoad is the method that gets called after
loadView finishes loading the view. This is a great place to continue your
interface setup.
ViewWillAppear
Gets called after the view is created and before the view is shown on the
screen. At this point, the view will still have its original size and won't match
ViewDidAppear
Gets called after the view is shown on the screen. At this point, the view
size will match the size of the screen.
ViewWillDisappear
viewWillDisappear gets called before the view will disappeared from the
screen. This can happen for three main reasons:
ViewDidDisappear
viewWillDisappear gets called after the view disappeared from the screen.
This will create a swift file with the UIViewController subclass representing
that controller. To use it you need to add it to the storyboard. Drag and drop
a View Controller Object from the Component Library next to your other
view controller(s):
You can make a separate file for this enum. You can instantiate a new view
controller by using the storyboard object:
Zen bits
To make your code less error prone, it's a good practice to keep all
your strings grouped in enums:
...
This will help you avoid making typos and needing to write long strings
without autocomplete :)
When the screen is done with its job, it can be dismissed by calling the
dismiss method:
secondary.dismiss(animated: true)
the usual case is that the secondary controller calls the dismiss
method on self
Mini Project
Create a new Xcode project with the Single View Application template.
Run the project now. When you tap on the button you should see an empty
screen.
Run the app now. You should be able to switch back and forth between
controllers.
UINavigationController
You cannot present another modal from a controller that was presented
modally. To do that you need to use a navigation controller. That's a view
controller that manages a stack of controllers that are displayed.
The first thing you need to do, in order to use a navigation controller, is to
add it to your storyboard. Drag and drop a Navigation Controller from the
Component Library :
Set your previous initial view controller as the root view controller of the
navigation controller:
Object template or if you haven't setup you initial controller use that View
Controller Object . You do this by connecting the rootViewController
Push
navigationController?.
pushViewController(secondary,
animated: true)
}
}
Pop
To push a view controller on the navigation stack you need to call
popViewController method on the navigation controller instance:
To make the navigation bar visible on a view controller object, you need to
set the Top Bar simulated metric:
This will make a navigation bar to appear on top of the view controller:
You can change the title from the Attributes Inspector or by setting the
title property on the view controller:
secondary.title = "Secondary"
Exercises
Pass a reference
Present controller and pass give it a reference of a label from the initial
view. The second screen has an input field and a Go Back button. Display
the inputed text in the second screen in the label from the initial screen.
When you open the app the label says Nothing yet :
And go back:
Solution
Inception
Make an app with a single view controller class. That controller shows a
label in the center indicating the deepness of the controller instance. Below
the label there's a button with the title Go Deeper . Every time you go
deeper, you push another instance of the same controller on the stack and
the level should increase by one, the initial level is 1.
Solution
Memory Game
The game shows 4 colored circles. Each circle makes a particular sound
when it is pressed or activated by the game. A round in the game consists
of the app lighting up one or more buttons in a random order, after which
the player must reproduce that order by pressing the buttons. As the game
progresses, the number of buttons to be pressed increases by one each
round, the first part of the sequence remains the same.
So the app might generate the sequence [.red] , if the user taps the red
circle, the app will add another color to the sequence, making it [.red,
.green] . After another succesfull round the sequence can become [.red,
.green, .red] . The sequence resets when the player makes a wrong
move.
You can generate tones by using system sounds, the phone pad sounds
are perfect for this game:
func play() {
if self != .None {
let systemSoundID = SystemSoundID(self.rawValue)
AudioServicesPlaySystemSound(systemSoundID)
}
}
}
You will need to import the AVFoundation framework in order to work with
system sounds.
Solution