I have a star button that I want to toggle between filled and empty when tapping.
I implemented the following function:
func setStarButton() {
let currentItem = data[currentItemIndex!]
if currentItem.isStarred == true {
starButton.setImage(UIImage(systemName: "star.fill"), for: .normal)
}
else {
starButton.setImage(UIImage(systemName: "star"), for: .normal)
}
}
@IBAction func starTapped(_ sender: Any) {
// Change the property in the Array
var currentItem = item[currentItemIndex!]
currentItem.isStarred?.toggle()
// Update the database
model.updateStarredStatus(currentItem.docID!, currentItem.isStarred!)
// Update the button
setStarButton()
}
I am then calling that function in the viewDidAppear:
override func viewDidAppear(_ animated: Bool) {
// Set the status of the star button
setStarButton()
}
So the following expected behaviour is happening:
What I am trying to make happen: for the star button to become filled/unfilled in the view as I continue to tap it in real time.
Any thoughts? I'm think I might need the view to listen for changes in the database to make visual update but I'm not sure. Any guidance is much appreciated!
Your item
array contains structs. Structs in Swift are value types. You effectively assign a copy of the struct to currentItem
when you say var currentItem = item[currentItemIndex!]
. This copy is then modified and thrown away when the function returns.
Later, when you access data[currentItemIndex!]
in setStarButton
you are accessing the original, unmodified struct. After you refresh the array from Firebase you see the correct value.
A simple way to avoid the problem is just to eliminate the local variable currentItem
and operate on the struct in the array directly.
Personally, I would rater pass the relevant value to setStarred
; that way you eliminate the duplicated code of access the items array. You should also avoid force unwrapping wherever possible. It also doesn't make a lot of sense for properties like isStarred
and docID
to be optional - An item is either starred or it isn't. A default value of false
makes more sense than nil
. All documents from Firebase will have a docID
func setStarButton(starred: Bool) {
let imageName = starred ? "star.fill" : "star"
starButton.setImage(UIImage(systemName: imageName), for: .normal)
}
@IBAction func starTapped(_ sender: Any) {
guard itemIndex = self.currentItemIndex else {
return
}
var isStarred = item[itemIndex].isStarred ?? false
isStarred.toggle()
item[itemIndex].isStarred = isStarred
// Update the database
model.updateStarredStatus(currentItem.docID!, isStarred)
// Update the button
setStarButton(starred: isStarred)
}
override func viewDidAppear(_ animated: Bool) {
// Set the status of the star button
if let itemIndex = self.currentItemIndex {
setStarButton(starred: self.item[itemIndex].isStarred ?? false)
}
}