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

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 new View 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 of UIViewController. Name it AddToDoViewController
  • 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

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 of ToDoTableVeiwController.swift and add the following code to create a reference to the AddToDoViewController. Notice we used VC, 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 of previousVC to the destination. The value of that property is the ToDoTableViewController, 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 the ToDoTableViewController 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 the CompleteToDoViewController on the Storyboard and open the Attributes Inspector. Let’s give this segue a name of moveToComplete in the Identifier 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).

  1. We will determine which ToDo row was tapped using toDos[indexPath.row]
  2. 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 our viewDidLoad function to grab the name of the ToDo and assign it to the text of our titleLabel
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