Iteration 1: Dynamic ToDos
Iteration 1: Dynamic ToDos
Learning Goals
- Add functionality so a user can add ToDos to list, and see the list updated
- Build a ToDo detail view, where user will eventually be able to delete a ToDo
Getting Started
- Make sure you’ve completed Iteration 0: Static To-Dos before starting this.
Design the AddToDoViewController
Now we’re going to build our AddToDoViewController
! Let’s first create it visually, then we’ll add some code to go with it.
- Drag a new
View Controller
from the Object Library to the Storyboard - Create a segue from the add button (+) on our
Table View Controller
to our newView Controller
(Action Segue ➡ Show) - this is going to allow us to move back and forth between views
Let’s think about what objects we will need to add to this view. Looking back at the finished app at the top of the page, we need…
- a label for
Title:
- a text field where the user can add a ToDo
- another label for
Important:
- a switch to toggle important/not important
- a button to add a ToDo
Stop here and drag the objects listed above onto the new View Controller. Don’t forget to add constraints so that it looks good on all screens!
Ideally, we probably would like our switch to be in the off
position when we come to the page. If we highlight the switch in our storyboard and open the Attributes Inspector, we can change the state of the switch to off.
Now is probably a good time to run this in your simulator and make sure everything looks how you want it to.
Set Up the AddToDoViewController file
- Now let’s add the code file that will be associated with this
View Controller
(File ➡ New ➡ File… ➡ Cocoa Touch Class ➡ Next) - this will be a subclass ofUIViewController
. Name itAddToDoViewController
- On your StoryBoard, select the
View Controller
and then open the Identity Inspector. For the Class dropdown, add the new class you just created to connect the code to the StoryBoard - Next, let’s just make the necessary outlet and action connections we need (Warning: do not just copy and paste; you need to drag and drop to properly establish these connections!).
- An action called
addTapped
connected to the button - An outlet called
titleTextField
connected to the text input - An outlet called
importantSwitch
connected to the switch
- An action called
Once that’s complete, the code in your AddToDoViewController.swift
file should look like this:
import UIKit
class AddToDoViewController: UIViewController {
@IBOutlet weak var titleTextField: UITextField!
@IBOutlet weak var importantSwitch: UISwitch!
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func addTapped(_ sender: Any) {
}
}
Adding ToDos
Now we want to work on adding a new ToDo from the AddToDoViewController
, then popping back to the ToDoTableViewController
and being able to see that new ToDo in our Table View
- First, we need to make a new ToDo object from the ToDo class inside our
addTapped
function. - Then, we’ll take the value of the input field and the importance status and share that information with our new ToDo object
@IBAction func addTapped(_ sender: Any) {
let toDo = ToDo()
if let titleText = titleTextField.text {
toDo.name = titleText
toDo.important = importantSwitch.isOn
}
}
Now we need to add this ToDo to our ToDo array. We have a small problem though… that ToDo array lives on another class (ToDoTableViewController
). No worries! We can fix this by adding a reference to that class in our AddToDoViewController
. All we need to do is add the following line of code above our outlets.
var previousVC = ToDoTableViewController()
Now in the ToDoTableViewController
, we need create a prepare
for segue function. This function will be called right after the user taps the “+” icon, but before the segue is actually triggered. It’s a built-in piece of functionality that Apple gives us.
- Un-comment out the
prepare
function at the bottom ofToDoTableVeiwController.swift
and add the following code to create a reference to theAddToDoViewController
. Notice we usedVC
, short for ViewController in the variable and property names
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let addVC = segue.destination as? AddToDoViewController {
addVC.previousVC = self
}
}
What is the code above doing? It is essentially saying:
- Once the user triggers a segue, check the destination.
- If the destination for this segue is the
AddToDoViewController
, then please assign a property ofpreviousVC
to the destination. The value of that property is theToDoTableViewController
, or the current View Controller. This allows the two View Controllers to know about each other!
Now back in the AddToDoViewController
, we can access the ToDo array that lives in the ToDoTableViewController
.
- Add the new ToDo to the array and update/reload the Table View:
@IBAction func addTapped(_ sender: Any) {
let toDo = ToDo()
if let titleText = titleTextField.text {
toDo.name = titleText
toDo.important = importantSwitch.isOn
}
previousVC.toDos.append(toDo)
previousVC.tableView.reloadData()
}
If we run our application right now, we can add a new ToDo, click
< ToDo List
, and our new ToDo is there in our Table View!!!
The only thing we have left to do for this part is to send the user back to the Table View once they’ve added a ToDo. We just need to call a single function after we update/reload the Table View.
We will add this line of code to our addTapped
function. It first checks if this View Controller is embedded in a Navigation Controller, then “pop”, or go back to the previous View Controller. It will provide a smooth animation if you provide a value of true
to the animation argument.
navigationController?.popViewController(animated: true)
- Pop back to the previous view when the user taps the
Add
button
@IBAction func addTapped(_ sender: Any) {
let toDo = ToDo()
if let titleText = titleTextField.text {
toDo.name = titleText
toDo.important = importantSwitch.isOn
}
previousVC.toDos.append(toDo)
previousVC.tableView.reloadData()
navigationController?.popViewController(animated: true)
}
Run your application in the simulator to make sure everything is working correctly.
Completing a ToDo
Now we want to focus on being able to select a ToDo from the Table View and being taken to another view where we can mark a ToDo complete and remove it from our list.
- Add another
View Controller
to your Storyboard (I added mine below theToDoTableViewController
since it will segue from there) - Select the
ToDoTableViewController
and create a segue from ToDo List (top, left icon in Table VC) to the new View Controller (Manual Segue ➡ Show) - this automatically gives us a nav item that can take us back to the ToDo List - Add a label for our ToDo and a complete button to the Storyboard
- Now let’s add the code file that will be associated with this View Controller (File ➡ New ➡ File… ➡ Cocoa Touch Class ➡ Next) - this will be a subclass of UIViewController. Name it
CompleteToDoViewController
- On your StoryBoard, select the new
View Controller
and then open the Identity Inspector. For the Class dropdown, add the new class you just created to connect the code to the StoryBoard
- Create the necessary outlet and action connections. You code should look like this once you’ve made those connections:
import UIKit
class CompleteToDoViewController: UIViewController {
@IBOutlet weak var titleLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func completeTapped(_ sender: Any) {
}
}
When the user taps on a single ToDo, we want to initiate the segue
from the ToDo List
to the CompleteToDoViewController
. In order for that to happen, we have to give the segue a name.
- Highlight the segue between the
ToDoTableViewController
and theCompleteToDoViewController
on the Storyboard and open the Attributes Inspector. Let’s give this segue a name ofmoveToComplete
in theIdentifier
field
Now we need to go back to the ToDoTableViewController
and add a tableView
function that has an argument of didSelectRowAt
. Inside of here, we want to call performSegue (you should be able to press Enter
to autocomplete this function with the correct arguments).
- We will determine which ToDo row was tapped using
toDos[indexPath.row]
- We will give instructions to perform the segue, with the additional information of that identifier “moveToComplete”
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// this gives us a single ToDo
let toDo = toDos[indexPath.row]
performSegue(withIdentifier: "moveToComplete", sender: toDo)
}
Run you code in the simulator. Tap on a row in your ToDo list. You should be taken to the View that you most recently made. However, the specifics of the ToDo that was tapped on are not yet showing up.
Customize Complete View
Let’s do a little more work so that when the user taps on a ToDo row, the CompleteToDoViewController
will show them information unique to the ToDo they tapped! First, we need to add two properties on our CompleteToDoViewController
class so that we can reference the (previous) ToDoTableViewController
.
var previousVC = ToDoTableViewController()
var selectedToDo = ToDo()
We need to revisit our prepare
for segue function that we wrote in our ToDoTableViewController
. This function gets called whether we segue to the AddToDoViewController
or segue to the CompleteToDoViewController
, so we need to be a little more specific.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let addVC = segue.destination as? AddToDoViewController {
addVC.previousVC = self
}
if let completeVC = segue.destination as? CompleteToDoViewController {
if let toDo = sender as? ToDo {
completeVC.selectedToDo = toDo
completeVC.previousVC = self
}
}
}
We’ve now successfully set up the segue and we need to make sure that the ToDo text is getting to the CompleteToDoViewController
.
- In
CompleteToDoViewController.swift
, we need to add some code to ourviewDidLoad
function to grab the name of the ToDo and assign it to the text of ourtitleLabel
override func viewDidLoad() {
super.viewDidLoad()
titleLabel.text = selectedToDo.name
}
Run your application on the simulator and make sure you are getting the ToDo in the CompleteToDoViewController. Everything should be working great now!
The only piece of functionality that we are missing is being able to remove a ToDo from the ToDo List (Table View). Since we are just working with hard-coded data at the moment, we would have to loop through the array and find the ToDo that we are wanting to remove. This is kind of a pain and won’t be necessary once we implement CoreData, so hang tight for a bit and we will add this functionality once we are dealing with real data in our mini-database in Iteration 2!
Commit Your Work
Commit your work to the Git repository. If you need to brush up on how to do that - no worries! It’s listed as Step 5 in the Git and GitHub walk-thru. Your commit message should be something like “Complete Iteration 1”.
Let’s keep going! Move on to Iteration 2