Home > OS >  How to display all items from a Set
How to display all items from a Set

Time:06-18

My app consists on an image of various foods, in which the user taps the image and adds this food into a Set<Food>.

I want to show all items from this Set inside the class called Favorites, as a: Text("You like: \(favorites.comidas)") but I can't manage to make it work

class Favorites: ObservableObject {
    var foods: Set<Food>
}
class Favorites: ObservableObject {
    var foods: Set<Food>
    
    init() {
        // load the saved data
        foods = []
    }
    
    func contains(_ food: Food) -> Bool {
        foods.contains(food)
    }
    
    func add(_ food: Food)  {
        objectWillChange.send()
        foods.insert(food)
        save()
    }
    
    func delete(_ food: Food) {
        objectWillChange.send()
        foods.remove(food)
        save()
    }
}
struct Food: Identifiable, Hashable {
    
    var id: Int
    let name: String
    let foodImage: [String]
    
    // Equatable
    static func == (lhs: Food, rhs: Food) -> Bool {
        lhs.id == rhs.id
    }
}

@EnvironmentObject var favorites: Favorites
let food: Food

var body: Some View {

Image(food.foodImage[0])
      .onTapGesture { 
       if favorites.contains(food) {
                 favorites.delete(food)
       } else {
          favorites.add(food)
       }
   }
}

CodePudding user response:

You haven't shown your Food structure, but I will assume it has a property, name.

ListFormatter is your friend with a task like this. Its string(from:[]) function takes an array and returns it in a nicely formatted list. You can use map to get an array of name strings from your set.

For the input array ["pizza","tacos","chocolate"] it will give "pizza, tacos and chocolate"

var favoriteList: String {
    let formatter = ListFormatter()
    let favorites = formatter.string(from:self.favorites.foods.map{$0.name})
    return favourites ?? ""
}

Then you can use this function in a Text view:

Text("You like \(self.favoriteList)")

Note that a Set is unordered, so it might be nice to sort the array so that you get a consistent, alphabetical order:

var favoriteList: String {
    let formatter = ListFormatter()
    let favorites = formatter.string(from:self.favorites.foods.map{$0.name}.sorted())
    return favourites ?? ""
}

Thanks to a tip from Leo Dabus in the comments, in Xcode 13 and later you can just use .formatted -

var favoriteList: String {
    return self.favorites.foods.map{$0.name}.sorted().formatted() ?? ""

}

CodePudding user response:

To answer your question How to display all items from a Set, you could try the following approach, works for me:

@main
struct TestApp: App {
    @StateObject var favorites = Favorites()  // <-- here
    let food: Food = Food(id: 1, name: "info", foodImage: ["info"])
    var body: some Scene {
        WindowGroup {
            ContentView(food: food)
             .environmentObject(favorites) // <-- here
        }
    }
}

struct Food: Identifiable, Hashable {
    var id: Int
    let name: String
    let foodImage: [String]
    
    // Equatable
    static func == (lhs: Food, rhs: Food) -> Bool {
        lhs.id == rhs.id
    }
}

struct ContentView: View {
    @EnvironmentObject var favorites: Favorites
    let food: Food
    
    var body: some View {
        List {
            ForEach(Array(favorites.foods)) { item in  // <-- here
                if let img = UIImage(systemName: item.foodImage.first ?? "questionmark") {
                    VStack {
                        Text("You like: \(item.name)")
                        Image(uiImage: img)
                            .onTapGesture { // here, strange logic, but that's what you want
                                if favorites.contains(food) {
                                    favorites.delete(food)
                                } else {
                                    favorites.add(food)
                                }
                            }
                    }
                }
            }
        }
        .onAppear {
            favorites.foods.insert(Food(id: 2, name: "globe", foodImage: ["globe"]))
            favorites.foods.insert(Food(id: 3, name: "folder", foodImage: ["folder"]))
        }
    }
}

class Favorites: ObservableObject {
    @Published var foods: Set<Food> = []  // <-- here
    
    init() { }
    
    func contains(_ food: Food) -> Bool {
        foods.contains(food)
    }
    
    func add(_ food: Food)  {
        foods.insert(food)
        save()
    }
    
    func delete(_ food: Food) {
        foods.remove(food)
        save()
    }
    
    func save() {
        // save your data here
    }
}
  • Related