Lesson 4 - Simple iOS calculator in Swift
In the previous lesson, Swift UI for different screen sizes and Autolayout, we tried to position components using
Autolayout
. It was still rather an introductory lesson.
In today's iOS tutorial, we'll finally get to programming! Let me reveal it'll be a simple calculator.
Before we start with the calculator itself, let's have a look at how to connect our UI (the individual components) to Swift code so we can work with them and, for example, react to a button being clicked.
Connecting the UI and code
To be able to change any component or get their state or data at runtime, we have to connect them to the application logic. Assistant Editor which we introduced in the last lesson will be useful for that.
First, make sure you have the Main.storyboard
file opened. You
can switch between editors in the top right corner - the icon of two overlapping
circles. When clicked, two editor windows will show up. The first one, on the
left, displaying the main storyboard and the second displaying the
ViewController.swift
file. This very file is connected to our
controller. Everything is already set in the project. We'll learn to create new
controllers, resulting in multiple screens, in further lessons. If the right
side editor isn't displaying the correct swift file, you can select one manually
in the navigation right above the editor. Choose Manual instead of Automatic and
click through the file.
Now we can finally start connecting the UI to the code. Select
Label
we added in the beginning of the course and while holding
Ctrl, hold the left mouse button and move the cursor to the editor
with Swift code. A blue line and the "Insert Outlet or Outlet Collection" text
will show up. Now release the mouse button and the dialog below will pop up:
Instead of Ctrl + the left mouse button, you can also right-click and drag without holding Ctrl.
All we have to do now is to enter the name of our component in the dialog,
e.g. myLabel
, and confirm by pressing "Connect":
Congratulations! You've just successfully connected a component to the code
and now your Label
is ready to be modified. There's the
viewDidLoad()
method prepared in the controller which is called
after initializing the UI controller. Here you can try to modify your
Label
. For example, let's change its text and color:
override func viewDidLoad() { super.viewDidLoad() myLabel.text = "Hello from Code!" myLabel.textColor = UIColor.red }
Right after the component is connected and
@IBOutlet
is created in the code, we have to be careful about
deleting this reference (the particular code line). For example, we if change
our mind and won't want to modify the Label
anymore, we could
delete the code line. However, if we do that, we also have to delete the
connection we've created. Otherwise, the application crashes when loading the
controller and the error message won't be much of a help.
To delete the connection, just right-click the Label
either
directly in the UI or in the list in the right column and delete the connection
in the Referencing outlets section.
Reacting to button clicks and other events
@IBOutlet
isn't the only way to connect the UI to the code. The
second is @IBAction
which reacts to user actions. The best example
would be a button, let's have a look at how to connect one to the code. Add a
new Button
into the controller and if you haven't opened the
Assistant Editor yet, do it now. The procedure is basically the same as with the
Label
above.
As soon as you drop the button into the code, an Outlet dialog pops up.
First, select Action in the Connection menu. Then we just fill in the name (of
the method that is going to be created). I personally always change the Type
from Any
to the control type I'm connecting, in this case
UIButton
. Click the "Connect" button and that's it. Your new method
will be executed every time the button is clicked. I personally also suffix the
button methods as Btn_Click
, so this example method could be named
as testBtn_Click
.
Creating a simple calculator
To try out the new stuff, let's make a trivial calculator. Create a new
Simple View App
project. I chose
SimpleCalculator_ICTsocial
as the project name.
Designing the UI
Now it's necessary to think about what we'll need in our calculator. We want
to perform operations with two numbers, which would mean two
Text Field
components, and also a Button
to perform
the calculation.
It's also a good idea provide a smart way for selecting math operations -
addition, subtraction, multiplication, and division. The PickerView
component will be perfect for this. Place the components into the prepared
View Controller
. The best would be if you placed them in the upper
part, since when entering numbers, a keyboard will be shown in the bottom part.
The result will look like this:
I set the controller background to blue to make the TextField
components more visible. To avoid setting the constraints to all the components,
we'll use StackView
.
StackView
The StackView
component makes it easy to stack components next
or under each other, without setting their constraints. You specify the
Alignment (either centered or via constraints) to this component only.
Except the Axis property, which determines the direction of the
components (vertical, horizontal), you'll also often use Alignment,
Distribution, and Spacing. The first one set alignments, the
second one determines how components of different sizes will behave, and the
third defines the spacing between components. Try to place several components
into the StackView
and test the properties out.
The simplest way to place the components into the StackView
is
by selecting them and clicking the Embed in Stack option in the
constraints menu we've already been using. The components may be a little
misplaced, but you can just play with this in the StackView
settings. We can also drop the StackView
from the component library
(it's named Vertical Stack View
and
Horizontal Stack View
, but we can change this orientation later)
and choose the direction right away. Let's place the components into the
StackView
.
Select all the components we added (by dragging the mouse or Cmd +
left mouse button in the list on the left) and click the Embed in Stack
button in the menu where we've been setting the constraints. This way, Xcode
basically creates a new Stack View
and places all the selected
components inside. Chores made easy. The result is the same as if we'd drop the
Stack View
from the library and place all the components inside
manually.
The result:
The components will be probably misplaced. Don't panic. First, let's set the
constraints of our new StackView
to 0
from the top,
bottom, left, and right edge.
Then we'll set constraints to our TextField
components, you can
select both of them by holding Cmd. We'll set the left and right
constraints to e.g. 10
. Let's also add some text to the button,
e.g. "Calculate". I also changed the font color to white because of the blue
background. We can set the Spacing
property of our
Stack View
to avoid having our components to close to each
other.
The user interface should look something like this:
A last detail. In Attributes inspector, let's set the
Keyboard Type
attribute of our TextField
s to
Number Pad
. This attribute can be found in the Text Input Traits
category. This way, the keyboard changes to a number pad when entering
numbers.
In Xcode, you can switch between individual device views and see that the UI is responsive towards it.
Code
We've finished the user interface, let's move to the code. Open the Assistant
Editor in Xcode and create outlets for our TextField
components and
for the PickerView
. Also create an action for the button click:
@IBOutlet weak var firstNumberInput: UITextField! @IBOutlet weak var secondNumberInput: UITextField! @IBOutlet weak var mathOperationPicker: UIPickerView! @IBAction func calculateBtnClick(_ sender: Any) { }
A simple action won't be enough for the PickerView
, instead, we
have to set our controller as a data source and delegate. We are provided with
two protocols for that, let's add them:
class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
Then we just have to set these attributes in the viewDidLoad()
method using self
:
override func viewDidLoad() { super.viewDidLoad() mathOperationPicker.dataSource = self mathOperationPicker.delegate = self }
We're basically telling the PickerView
that our controller will
take care of the data and handle the component by itself.
We can't build the app now, we still have to implement two methods. Xcode will help us by generating them:
func numberOfComponents(in pickerView: UIPickerView) -> Int { return 1 } func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return 4 }
Because we know how many components and rows we'll have, we can "hard-code"
the numbers. The last method remaining isn't necessary for a correct build.
We'll set what should the PickerView
display:
let mathOperations = ["+", "-", "*", "/"] func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { return mathOperations[row] }
We can try to run the app to make sure everything works fine. Design isn't the best (it's horrible, actually), but that's not the point of today's tutorial:
Now let's implement the action of the "Calculate" button:
@IBAction func calculateBtnClick(_ sender: Any) { let firstNumber = Int(firstNumberInput.text!)! let secondNumber = Int(secondNumberInput.text!)! let selectedMathOperation = mathOperationPicker.selectedRow(inComponent: 0) var result : Int switch selectedMathOperation { case 0: result = firstNumber + secondNumber case 1: result = firstNumber - secondNumber case 2: result = firstNumber * secondNumber case 3: result = firstNumber / secondNumber default: result = 0 } displayMessage(message: String(result)) }
Finally, we'll implement the displayMessage()
method displaying
the result in a dialog with a close button. I chose this approach to show the
usage of simple dialogs:
func displayMessage(message: String) { let alertController = UIAlertController(title: “Result”, message: message, preferredStyle: UIAlertControllerStyle.alert) alertController.addAction(UIAlertAction(title: “Close”, style: UIAlertActionStyle.default,handler: nil)) self.present(alertController, animated: true, completion: nil) }
Now, let's try calculating something:
Tip
As I mentioned before, Autolayout
is complex and probably
annoying for beginners. But it's something that you're going to encounter all
the time when developing for iOS, so it's necessary to get comfortable with.
Before you continue with the next lesson, try everything out. Choose few of
your favorite apps and try to "copy" their UI in your project. All you have to
do is to place the right components at the same spot and set constraints. Then
switch between devices in the bottom part of Xcode and check whether everything
works the way it should - components being at the right place, no unwanted
spaces, and no other problems. You can also represent the components by Views of
different colors, because what we're trying to practice is positioning and
correct Autolayout
settings.
Show that you're serious with iOS development and let us see the screenshots of your work in the comments section below
In the next lesson, Introduction to the important TableView component, we'll go over TableView
.