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
}
}