It's usually better design to separate the model from the views. That is, it's better design (in your case) to store the user's vote (like/dislike/none) and the vote counts directly, instead of implicitly as the button states and labels.
class VotableItem {
// Votes: -1 for dislike, 1 for like, 0 for no vote
var vote: Int = 0 // initialize to user's existing vote, retrieved from the server
var likeCount: Int = 0 // initialize to existing like count, retrieved from the server
var dislikeCount: Int = 0 // initialize to existing dislike count, retrieved from the server
You'll also need outlets connected to the like and dislike buttons, to update them:
@IBOutlet var likeButton: UIButton!
@IBOutlet var dislikeButton: UIButton!
When the user clicks the Like button, there are two possible actions. If the existing vote was “like” (stored as 1), she wants to undo her “like” vote and set her vote back to “none” (stored as 0). Otherwise, the existing vote was either “none” or “dislike” (stored as -1), and she wants to undo her existing vote and set her vote to “like”.
Similarly, when the user clicks the Dislike button, there are two possible actions. If her existing vote was “dislike”, she wants to undo it, setting it to “none”. Otherwise, she wants to undo her existing “like” or “none” vote, and set her vote to “dislike”.
Note that undoing a “none” vote has no effect, but the logic is simpler if we allow undoing it (by doing nothing).
Since the actions of the Like and Dislike buttons are so similar, we can factor out the common behavior. Connect the buttons to these actions:
@IBAction func dislikeButtonWasClicked(sender: UIButton) {
buttonWasClickedForVote(-1)
}
@IBAction func likeButtonWasClicked(sender: UIButton) {
buttonWasClickedForVote(1)
}
Then implement the buttonWasClickedForVote(_:)
function:
private func buttonWasClickedForVote(buttonVote: Int) {
if buttonVote == vote {
// User wants to undo her existing vote.
applyChange(-1, toCountForVote: vote)
vote = 0
}
else {
// User wants to force vote to toggledVote.
// Undo current vote, if any.
applyChange(-1, toCountForVote: vote)
// Apply new vote.
vote = buttonVote
applyChange(1, toCountForVote: vote)
}
updateUserInterfaceFromModel()
}
I'm using a helper function to update the counts. Note that it does nothing if the vote is 0 (meaning “none”):
private func applyChange(change: Int, toCountForVote vote: Int ) {
if vote == -1 { dislikeCount += change }
else if vote == 1 { likeCount += change }
}
And we need another function to make the UI state match the model:
private func updateUserInterfaceFromModel() {
likeButton.selected = vote == 1
likeButton.setTitle("\(likeCount)", forState: .Normal)
dislikeButton.selected = vote == -1
dislikeButton.setTitle("\(dislikeCount)", forState: .Normal)
}
Note that the updateUserInterfaceFromModel
function doesn't depend on the current state of the user interface. That means you can use it during initialization too. Just set vote
, likeCount
, and dislikeCount
(presumably based on data from the server). Then, in viewDidLoad
, call updateUserInterfaceFromModel
.