So far we’ve been focusing on having the Table View display static content. Now that it can do that correctly, we can extend it to be dynamic.
Adding @IBActions
@IBActions are very similar to @IBOutlets, except actions represent functions. We can connect our add and subtract buttons to @IBAction
functions. When the button is pressed, those functions will be executed.
Open up your code and storyboard side-by-side again. You want to control-drag
from the subtract button to the code. This time, though, you want to change it from an outlet to an action.
You can name the function anything you want, but it should definitely include a verb. Press connect, and you should get a function:
@IBAction func subtractPressed(_ sender: AnyObject) {
}
If you accidentally made an @IBOutlet
, it isn’t enough to just delete the line of code. You must remove the outlet from the button’s Connection Inspector:
If you don’t remove the connection in the storyboard, your app will crash when you launch it. This is one of the most common bugs we see at iOS Club, but it’s easy enough to fix.
Repeat the process for your add button.
Saving the arguments from the decorate
method
The add and subtract buttons are supposed to change the person’s score and then update the label, but we can’t change the person’s score if the buttons don’t know which person they’re controlling. The decorate
method knows, though, so we want to hold on to that information. We can make a small change to that method:
var name: String!
var controller: ViewController!
func decorate(for name: String, in controller: ViewController) {
let score = controller.scores[name] ?? 0
self.label.text = "\(name): \(score)"
self.name = name
self.controller = controller
}
This way we can access the name
and controller
in our addPressed
and subtractPressed
methods.
Writing a helper method
The add and subtract buttons both do the same general operation: change the score and then update the label. The add button adds 1 to the score. The subtract button subtracts 1 from the score, which is really just adding -1.
We can write a helper method to do this instead of duplicating code between the two methods:
func updateScore(offset: Int) {
var score = controller.scores[name] ?? 0
score += offset
controller.scores[name] = score
self.label.text = "\(name): \(score)"
}
The method reuses patterns we’ve already seen elsewhere, so it’s easy enough to interpret. It retrieves the current score, updates it, saves the new value to the dictionary, and then updates the label.
We’ll update the add and subtract methods to use this new helper method:
@IBAction func subtractPressed(_ sender: AnyObject) {
self.updateScore(offset: -1)
}
@IBAction func addPressed(_ sender: AnyObject) {
self.updateScore(offset: 1)
}
Checking our progress
Everything should be in place for the user interaction. Run the app in the simulator and check it out.
Everything looks normal at first, but then we start pressing the buttons:
Brian
has turned in to Optional("Brian")
. This is Swift being particularly cautious.
When we reference name
in the updateScore
method, we’re referencing the class’s var name: String!
. The type String!
is an Implicitly Unwrapped Optional, meaning it may be nil
but Swift will let you interact with it as if it’s a non-optional String
type. This effectively erases the safety benefits we get from working with optionals, but is useful in contexts where we know something won’t be nil by design.
In some situations, Swift takes the safe approach and treats String!
like String?
. In that instance, Brian
turns in to Optional("Brian")
. We need to unwrap the optional value so it’s unambiguously non-nil.
We’ve used the nil-coalescing operator before, and this is another situation where it shines. We can change the line in updateScore
to self.label.text = "\(name ?? "Unnamed Person"): \(score)"
. That way, in the off-chance that name
is somehow nil
, it will be replaced with "Unnamed Person"
.
If we run the app again, we see that everything is peachy:
Recap
You’ve built an interactive Table View app using constraints, Table View delegates, custom Table View cells, and custom behavior. Great job!
If you have any feedback on this tutorial, found something particularly difficult to understand, or got stuck on a weird bug, you can reach out to us on our Facebook group.
https://www.facebook.com/groups/iosgatech/