Learn Courses My Dashboard

Button image not changing on clicking until i click on another button

Hello, I have a HStack with 2 buttons that changes image upon click. The first button does not change on clicking until I click the other button, the second button updates correctly. The difference between the 2 buttons is that the first one’s action depends on the variable ‘museum.favorite’, which is a variable of the class Museum. The second one updates depending on the variable b’s value, which is a state variable declared in the same view.
I have been stuck on this for the past few days and would really appreciate if any one knows the reason behind & a way around this. The reason I want the button to update depending on the value of ‘museum.favorite’ is because I found that if I update according to a state variable declared in the same view (passing the variable as a parameter does not work either), then the image changes back to the default one (“heart” instead of “heart.fill”) soon after I change to another view.

Thank you so much for reading this post! :slight_smile:

It’s because museum.favorite isn’t being observed by your View. You need to make museum an @StateObject or @ObservedObject with favorite as an @Published property so that SwiftUI will watch it and react to changes you make.

2 Likes

I have tried this already, but adding @Published to favourite gets the error: “Type ‘Museum’ does not conform to protocol ‘Decodable’”. The class Museum is declared as:

class Museum : Identifiable, Decodable, ObservableObject {}.

How can I solve this?

Hi Stella,

Take a look at this sample project which is based on some guesses I made about what you might be trying to do. You might want to change the Signing & Capabilities team to your Team before you run it.

Let me know if there is anything you don’t understand.

1 Like

Hi Chris_Parker,

Thank you so much for the sample project! I’ve run it and it is indeed very similar to what I am trying to do. However, I do have some questions:

  1. Under UpdateFavouriteView, why doesn’t the heart image updates on click? I understand that UpdateFavouriteView is linked under ContentView, and if you start from ContentView → UpdateFavouriteView, then the image changes on click.

  2. Is there a way the heart image in ContentView could be changed directly on click without being directed to UpdateFavouriteView to update it?

These 2 questions are perhaps the same, but this is essentially the part that I am stuck on.
Thank you so much for your time again :slight_smile:

Hi Stella,

Yes, absolutely the favourite can be updated from ContentView rather than going to another screen altogether. I was basing the code on what appeared to be a second view where you were passing in a museum as in a Detail View.

Change you ContentView to this:

struct ContentView: View {
    @EnvironmentObject var model: MuseumViewModel

    var body: some View {
        NavigationView {
            List {
                ForEach(model.museums) { museum in
                    HStack {
                        Text(museum.name)
                        Spacer()
                        Image(systemName: museum.favourite ? "heart.fill" : "heart")
                            .foregroundColor(.red)
                            .onTapGesture {
                                model.updateFavourite(museumId: museum.id)
                            }
                    }
                }
            }
            .listStyle(PlainListStyle())
            .navigationTitle("Museums")
        }
    }
}

Thank you so much! It works now.
I did find a different approach. In case any one is interested, this is what I did in the end:

 import SwiftUI
    
    struct buttons: View {
        
        var museum:Museum
        @State var visitIcon = "figure.walk.circle"
        @State var heart = "heart"
        
        var body: some View {
            HStack(spacing:60){
                    Button(action: {
                        museum.visited.toggle()
                        if(museum.visited){
                            visitIcon = "figure.walk.circle.fill"
                        }
                        else{
                            visitIcon = "figure.walk.circle"
                        }
                    }, label: {
                        VStack{
                            Image(systemName:visitIcon).resizable()
                                    .frame(width: 20, height: 20).foregroundColor(.black)
                            
                            Text("Visited").foregroundColor(.black)
                        }
                    })
                    Button(action: {
                        museum.favorite.toggle()
                        if(museum.favorite){
                            heart = "heart.fill"
                        }
                        else{
                            heart = "heart"
                        }
                    }, label: {
                    
                        VStack{
                            Image(systemName:heart).resizable()
                                .frame(width: 20, height: 20).foregroundColor(.black)
                            Text("Favorite").foregroundColor(.black)
                        }
                    })
            }.onAppear {
                if(museum.favorite){
                    visitIcon = "figure.walk.circle.fill"
                }
                else{
                    visitIcon = "figure.walk.circle"
                }
                if(museum.favorite){
                    heart = "heart.fill"
                }
                else{
                    heart = "heart"
                }
            }
        }
    }

the important part is to add .onAppear at the end so Xcode checks & updates the image across different views :slight_smile: