Home > Mobile >  How we can change text of button when a button is clicked for list items in SwiftUI?
How we can change text of button when a button is clicked for list items in SwiftUI?

Time:03-08

I have list view in SwiftUI, and when I use context menu, I want to put the star icon for item as a Favorite or Unfavorite , when I click the text button, it show the Favorite text, but when I click the another item it show Unfavorite text, I do not know how to solve it, any idea?

favoritemodel:

import SwiftUI

struct FavoriteModel{
    var isFavorite: Bool = false
    var timeStamp: Date = Date()
    var userIds: Set<UUID> = []
    mutating func toogleFavorite(userId: UUID){
        isFavorite.toggle()
        timeStamp = Date()
        
        if isFavorite{
            userIds.insert(userId)
        }else{
            userIds.remove(userId)
        }
    }
}

model:

struct Restaurant: Identifiable {
    var id = UUID()
    var name: String
    var image: String
    var isFavorite: Bool = false
  
}

BasicImageRow:

struct BasicImageRow: View {

    var restaurant: Restaurant
    
    var body: some View {
        HStack {
            Image(restaurant.image)
                .resizable()
                .frame(width: 40, height: 40)
                .cornerRadius(5)
            Text(restaurant.name)
          
            if restaurant.isFavorite {
                Spacer()
                
                Image(systemName: "star.fill")
                    .foregroundColor(.yellow)
            }
        }
    }
}

view:

import SwiftUI

struct ContentView: View {
    
    @State var showAnswer = false
    @State var toggleText = false

    @State private var selectedRestaurant: Restaurant?
    
  @State private  var restaurants = [ Restaurant(name: "Cafe Deadend", image: "cafedeadend"),
                  Restaurant(name: "Homei", image: "homei"),
                  Restaurant(name: "Teakha", image: "teakha"),
                  Restaurant(name: "Cafe Loisl", image: "cafeloisl"),
                  Restaurant(name: "Petite Oyster", image: "petiteoyster"),
                  Restaurant(name: "For Kee Restaurant", image: "forkeerestaurant"),

            ]

 
    var body: some View {
        List {
            ForEach(restaurants) { restaurant in
                BasicImageRow(restaurant: restaurant)
                    .contextMenu {
                        
                        Button(action: {
                           self.showAnswer = true
                             self.toggleText.toggle()
                            self.setFavorite(item: restaurant)
                        }) {
                            HStack {
                               Text(toggleText ? "Favorite" : "UnFavorite")
    
                               Text(toggleText ? "UnFavorite" : "Favorite")

                                Image(systemName: "star")
                            }
                        }
                    }
                    .onTapGesture {
                        self.selectedRestaurant = restaurant
                    }
                    .actionSheet(item: self.$selectedRestaurant) { restaurant in
                        
                        ActionSheet(title: Text("What do you want to do"), message: nil, buttons: [
                            
                            .default(Text("Mark as Favorite"), action: {
                                self.setFavorite(item: restaurant)
                            }),
                            
                           
                            .cancel()
                        ])
                    }
            }
           
        }
    }
   
    
    private func setFavorite(item restaurant: Restaurant) {
        if let index = self.restaurants.firstIndex(where: { $0.id == restaurant.id }) {
            self.restaurants[index].isFavorite.toggle()
        }
    }
   
}

CodePudding user response:

If I understand the question correctly, try this approach by removing all toggleText and using @Ali Momeni advice:

struct ContentView: View {
    @State var showAnswer = false
    
    @State private var selectedRestaurant: Restaurant?
    
    @State private var restaurants = [ Restaurant(name: "Cafe Deadend", image: "cafedeadend"),
                                       Restaurant(name: "Homei", image: "homei"),
                                       Restaurant(name: "Teakha", image: "teakha"),
                                       Restaurant(name: "Cafe Loisl", image: "cafeloisl"),
                                       Restaurant(name: "Petite Oyster", image: "petiteoyster"),
                                       Restaurant(name: "For Kee Restaurant", image: "forkeerestaurant")
    ]
    
    private func RestaurantRow(restaurant: Restaurant) -> some View {
        BasicImageRow(restaurant: restaurant)
            .contextMenu {
                Button(action: {
                    showAnswer = true
                    setFavorite(item: restaurant)
                }) {
                    HStack {
                        Text(restaurant.isFavorite ? "UnFavorite" : "Favorite")
                        Image(systemName: "star")
                    }
                }
            }.id(restaurant.id)
            .onTapGesture {
                selectedRestaurant = restaurant
            }
            .actionSheet(item: $selectedRestaurant) { restaurant in
                ActionSheet(title: Text("What do you want to do"),
                            message: nil,
                            buttons: [
                                .default(
                                    Text(restaurant.isFavorite ? "Mark as UnFavorite" : "Mark as  Favorite"),
                                    action: {
                                        setFavorite(item: restaurant)
                                    }),
                                .cancel()
                            ])
            }
    }
    
    var body: some View {
        List {
            ForEach(restaurants) { restaurant in
                if restaurant.isFavorite {
                    RestaurantRow(restaurant: restaurant)
                } else {
                    RestaurantRow(restaurant: restaurant)
                }
            }
        }
    }
    
    private func setFavorite(item restaurant: Restaurant) {
        if let index = restaurants.firstIndex(where: { $0.id == restaurant.id }) {
            restaurants[index].isFavorite.toggle()
        }
    }
    
}

CodePudding user response:

I recommend changing your model to be like how Apple's Fruta sample implements favourites. E.g.

class Model: ObservableObject {
    ...
    @Published var favoriteSmoothieIDs = Set<Smoothie.ID>()

    func toggleFavorite(smoothieID: Smoothie.ID) {
        if favoriteSmoothieIDs.contains(smoothieID) {
            favoriteSmoothieIDs.remove(smoothieID)
        } else {
            favoriteSmoothieIDs.insert(smoothieID)
        }
    }

    func isFavorite(smoothie: Smoothie) -> Bool {
        favoriteSmoothieIDs.contains(smoothie.id)
    }


struct SmoothieFavoriteButton: View {
    @EnvironmentObject private var model: Model
    
    var isFavorite: Bool {
        guard let smoothieID = model.selectedSmoothieID else { return false }
        return model.favoriteSmoothieIDs.contains(smoothieID)
    }
    
    var body: some View {
        Button(action: toggleFavorite) {
            if isFavorite {
                Label {
                    Text("Remove from Favorites", comment: "Toolbar button/menu item to remove a smoothie from favorites")
                } icon: {
                    Image(systemName: "heart.fill")
                }
            } else {
                Label {
                    Text("Add to Favorites", comment: "Toolbar button/menu item to add a smoothie to favorites")
                } icon: {
                    Image(systemName: "heart")
                }

            }
        }
        .disabled(model.selectedSmoothieID == nil)
    }
    
    func toggleFavorite() {
        guard let smoothieID = model.selectedSmoothieID else { return }
        model.toggleFavorite(smoothieID: smoothieID)
    }
}

struct SmoothieFavoriteButton_Previews: PreviewProvider {
    static var previews: some View {
        SmoothieFavoriteButton()
            .padding()
            .previewLayout(.sizeThatFits)
            .environmentObject(Model())
    }
}

As you can see using structs for model data does require some re-thinking.

  • Related