Average Happiness is a single view application that allows the user to calculate their average happiness. Users can also toggle on and off which entries are included in the calculation.
The story board for this app is really simple. It consists of a TableviewController
and a custom UITableViewCell
Our Model will contain three properties
* title: String
* What the user will see displayed for the name of the cell
* happiness: Int
* How much Happiness this activity gives us
* isIncluded: Bool
* Keeps track of whether or not this Entry
is calculated into our averageHappiness
- Create a new
.swift
file calledEntry
- Inside of
Entry
add the following properties
// Mark: - Properties
let title: String
let happiness: Int
var isIncluded: Bool
- Create our Initializer
init(title: String, happiness: Int, isIncluded: Bool) {
self.title = title
self.happiness = happiness
self.isIncluded = isIncluded
}
Our Model Controller will have a…
* Shared Instance called shared
* An array of Entry
called entries
* a function that will update our entries
called updateEntry
- Create a new .swift file called
EntryController
- Inside of our
EntryController
class create a shared instance calledshared
static let shared = EntryController()
- Create a computed property that is an array of
Entry
. Let the students create their own entries that they want to put is, or set up your own to send to them
var entries: [Entry] = {
var entry1 = Entry(title: “Reading”, happiness: 7, isIncluded: true)
var entry2 = Entry(title: “Riding my bike”, happiness: 10, isIncluded: false)
var entry3 = Entry(title: “Waking up”, happiness: 1, isIncluded: true)
var entry4 = Entry(title: “Reading documentation”, happiness: 10, isIncluded: false)
return [entry1, entry2, entry3, entry4]
}()
- Create a function called
updateEntry
that takes in anEntry
and toggles whether itsisIncluded
property istrue
orfalse
func updateEntry(entry: Entry) {
entry.isIncluded = !entry.isIncluded
}
Our custom cell with contain the UI elements of our UITableViewCell
and update the data within them
- Create a new
Cocoa Touch Class
calledEntryTableViewCell
that is a subclass ofUITableViewCell
- Delete all existing functions within our new cell class
- Drag out outlets for all UI elements on our tableview cell
- be sure to subclass our Cell as type
EntryTableViewCell
- be sure to subclass our Cell as type
@IBOutlet weak var titleLabel: UILabel!
@IBOutlet weak var higherorLowerLabel: UILabel!
@IBOutlet weak var isEnabledSwitch: UISwitch!
- Create a variable called
entry
of an optional typeEntry
var entry: Entry?
- Create a variable called
averageHappiness
of typeInt
with a default value of0
var averageHappiness: Int = 0
- Create a function called
setEntry
that will take in anEntry
and anInt
. Our passed inEntry
will be set to our localentry
, we will useaverageHappiness
later.
func setEntry(entry: Entry, averageHappiness: Int) {
self.entry = entry
}
- Create a function called
updateUI
that takes in anInt
. This will update the UI Elements on ourUITableViewCell
. Note that we are not setting ourhigherorLowerLabel
yet as we don’t have a function to detect what it should be set too. We are building that next.
func updateUI(averageHappiness: Int) {
guard let entry = entry else {return}
titleLabel.text = entry.title
isEnabledSwitch.isOn = entry.isIncluded
}
- Call the function
updateUI(averageHappiness: Int)
at the bottom of oursetEntry
function and pass in ouraverageHappiness
- Create a function called
calcHappiness
that takes in a parameter calledaverageHappiness
of typeInt
and return aString
. We will use this function to set outhigherorLowerLabel
’s text. Inside the function create way to detect whether the average happiness is higher, Lowe, or equal to our entries happiness.
guard let entry = entry else {return “Error: Happines Not Found”}
switch entry.happiness {
case let happiness where happiness > averageHappiness:
return “Higher”
case let happiness where happiness == averageHappiness:
return “Average”
case let happiness where happiness < averageHappiness:
return "Lower"
default:
return “Error: Happines Not Found”
- Back in our
updateUI
function add a line that sets ourhigherorLowerLabel.text
equal to the result ofcalcHappiness
passing in the valueaverageHappiness
higherorLowerLabel.text = calcHappiness(averageHappiness: averageHappiness)
- Create a new
Cocoa Touch Class
calledEntryListTableViewController
- be sure to subclass our TableView as type
EntryListTableViewController
- be sure to subclass our TableView as type
- Remove all functions except
ViewDidLoad
numberOfRowsInSection
cellForRowAt
- Create an
IBOutlet
for the label at the top of our table view@IBOutlet weak var happinessLabel: UILabel!
- Create a variable called
averageHappiness
of typeInt
, give it a preset value of0
- In our
numberOfRowsInSection
returnEntryController.shared.entries.count
- In our
cellForRowAt
guard let our cell to be of typeEntryTableViewCell
guard let cell = tableView.dequeueReusableCell(withIdentifier: “EntryCell”, for: indexPath) *as*? EntryTableViewCell *else* {*return* UITableViewCell()}
- Grab our entry and pass it to our
cell.setEntry
//grabbing out entries
let entries = EntryController.shared.entries
//grabbing the entry that we want
let entry = entries[indexPath.row]
//passing our entry to out function setEntry
cell.setEntry(entry: entry, averageHappiness: averageHappiness)
- Run our app to make sure everything is working. We should see stuff populate
Explain Protocols and Delegates Analogy that you can use * Intern * Has a list of stuff that the boss might want done and can tells the workers to do it * Boss * Tells his intern what he wants done and might give some information for the intern to pass on, (ex: and Int of bool) * Worker * Is prepared to do anything the boss might want to do * Gets told by the intern when to do a certain task
- Back on our
EntryTableViewCell
declare a new protocol calledEntryTableViewCellProtocol
of typeclass
above ourEntryTableViewCell
class - Inside our protocol declare a function called
tappedCell
that takes in aEntrytableViewCell
- Inside our
EntryTableViewCell
class create a new property calleddelegate
of typeEntryTableViewCellProtocol?
- Create an
IBAction
for theUISwitch
with a sender ofUISwitch
- Inside the
UIAction
call our delegatestappedCell
function passing inself
- Back on our
EntryListTableViewController
create an extension of typeEntryTableViewCellProtocol
and conform to all protocols. - Inside of our newly made
tappedCell
function add a print statement for that says “TappedCell” for testing purposes - In our
CellForRowAt
addcell.delegate = self
before we return the cell - Run the app and make sure that “TappedCell” is being printed
- Create a new function in our
EntryListTableViewController
calledupdateHappiness
that recalculates the average happiness
func updateHappiness() {
var happinessTotal = 0
//loops through all of our entries
for entry in EntryController.shared.entries {
//If our entrys isIncluded is == true, then we add its happiness to happiness total
if entry.isIncluded {
happinessTotal += entry.happiness
}
}
//calculates our average happienss
averageHappiness = happinessTotal / EntryController.shared.entries.count
}
- Back inside our
tappedCell
function we need to update our update our cells info and our average Happiness, run the app
guard let indexPath = tableView.indexPath(for: cell) else {return}
let entry = EntryController.shared.entries[indexPath.row]
EntryController.shared.updateEntry(entry: entry)
updateHappiness()
cell.updateUI(averageHappiness: averageHappiness)
Explain Protocols and Delegates Analogy that you can use * Notifications are like a radio tower, and observers are like a radio, observers can only hear the station that they are tuned into
-
On our
EntryListTableViewController
add a constant called notification key above where we declare the class. Set it equal toNotification.Name(rawValue: “didChangeHappiness”)
let notificationKey = Notification.Name(rawValue: “didChangeHappiness”)
-
On our
averageHappiness
add a didSet that septs our a notification every time we update the variable. For the object passaveragehappiness
. Also update our happinessLabel
var averageHappiness: Int = 0 {
/*
Everytime that we set out happiness level we post a notification that contains out notificationKey and our averageHappiness
*/
didSet {
NotificationCenter.default.post(name: notificationKey, object: averageHappiness)
happinessLabel.text = "Average Happiness: \(averageHappiness)"
}
}
- On our
EntryTableViewCell
create a function calledaddObserver
that adds an observer listening for ournotificationKey
func createObserver() {
NotificationCenter.default.addObserver(self, selector:
#selector(self.recalcHappiness), name: notificationKey, object: nil)
}
- Add a de initializer that removes our observer once the cell is no longer in memory
deinit {
NotificationCenter.default.removeObserver(self)
}
- Next add an objC function called
recalcHappiness
that updates our cells average happiness label
@objc func recalcHappiness(notification: NSNotification) {
guard let averageHappiness = notification.object as? Int else {return}
higherorLowerLabel.text = calcHappiness(averageHappiness: averageHappiness)
}
- Run app and make sure everything is working