1

I am building an app that will store multiple Nintendo consoles and their details (kinda like Mactracker but for Nintendo stuff).

I have a favourites view that stores only if consoles that only have a bool variable set to true, the consoles are stored in a consoles file which will be showed below. This is all working fine but I want to make a button that changes the value of the bool from false to true and then from true to false if its tapped again.

I tried using .toggle() but couldn't get anywhere.

I want the console in which I press the "Add to favorites button" to save it in the favorites view. Which means I need to change a favourites bool in the console list to true. When I press on the same button again I want it to be removed from the favorites view

The button is in the ConsoleDetailView. I have a main menu which has the multiple categories including the favorites category which takes me to the favourites view.

Here's the main menu (The else if's help me put them in different sections so don't mind them):

import SwiftUI

struct MainMenu: View {
    // Use categories ordered by alphabetical order
    var categories = ConsoleList.categories.sorted(by: {$0.key < $1.key})
    @State var con = ConsoleList.consoles
    
    var body: some View {
        // Navigation view for all types
        NavigationView{
            List {
                Section {
                    NavigationLink {
                        CurrentConsoles(con: con)
                    } label: {
                        Image(systemName: "folder")
                            .foregroundColor(.red)
                        Text("Current Consoles")
                        .fontWeight(.semibold)}
                    NavigationLink {
                        Favorites(con: $con)
                    } label: {
                        //Favorites part
                        Image(systemName: "heart")
                            .foregroundColor(.red)
                        Text("Favorites")
                        .fontWeight(.semibold)}
                }
                Section {
                    ForEach(categories, id: \.key) {  category in
                        if category.key == "Home Consoles"{
                            NavigationLink(destination: ConsoleMenu(con: category.value), label:{
                                Image(systemName: "folder")
                                    .foregroundColor(.red)
                                Text(category.key)
                                    .fontWeight(.semibold)
                            })
                        }else if category.key == "Hybrid"{
                            NavigationLink(destination: ConsoleMenu(con: category.value), label:{
                                Image(systemName: "folder")
                                    .foregroundColor(.red)
                                Text(category.key)
                                    .fontWeight(.semibold)
                            })
                        }else if category.key == "Color TV Game"{
                            NavigationLink(destination: ConsoleMenu(con: category.value), label:{
                                Image(systemName: "folder")
                                    .foregroundColor(.red)
                                Text(category.key)
                                    .fontWeight(.semibold)
                            })
                        }else if category.key == "Other"{
                            NavigationLink(destination: ConsoleMenu(con: category.value), label:{
                                Image(systemName: "folder")
                                    .foregroundColor(.red)
                                Text(category.key)
                                    .fontWeight(.semibold)
                            })
                        }
                    }
                }
                Section {
                    ForEach(categories, id: \.key) {  category in
                        if category.key == "Nintendo DS / 3DS"{
                            NavigationLink(destination: ConsoleMenu(con: category.value), label:{
                                Image(systemName: "folder")
                                    .foregroundColor(.red)
                                Text(category.key)
                                    .fontWeight(.semibold)
                            })
                        }else if category.key == "GameBoys"{
                            NavigationLink(destination: ConsoleMenu(con: category.value), label:{
                                Image(systemName: "folder")
                                    .foregroundColor(.red)
                                Text(category.key)
                                    .fontWeight(.semibold)
                            })
                        }else if category.key == "Game & Watch" {
                            NavigationLink(destination: ConsoleMenu(con: category.value), label:{
                                Image(systemName: "folder")
                                    .foregroundColor(.red)
                                Text(category.key)
                                    .fontWeight(.semibold)
                            })
                        }else if category.key == "iQue" {
                            NavigationLink(destination: ConsoleMenu(con: category.value), label:{
                                Image(systemName: "folder")
                                    .foregroundColor(.red)
                                Text(category.key)
                                    .fontWeight(.semibold)
                            })
                        }
                    }
                }
            }
            .navigationTitle("Famitrack")
            
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        MainMenu()
    }
}

Here's the favourites view:

import SwiftUI

struct Favorites: View {
    
    @Binding var con: [ConsoleDetails]
    var body: some View {
        //list of consoles
        List($con){ $cons in
            if cons.favorites {
                NavigationLink(destination: ConsoleDetailView(con: $cons), label:{
                    Image(cons.imgName)
                        .resizable()
                        .scaledToFit()
                        .frame(width: 50, height: 50)
                        .cornerRadius(4)
                        .padding(.vertical, 4)
                    
                    VStack (alignment: .leading){
                        if cons.category == "Game & Watch" {
                            Text(cons.consoleName)
                                .fontWeight(.semibold)
                            Text(cons.mostSoldGame)
                                .font(.subheadline)
                        }else{
                            Text(cons.consoleName)
                                .fontWeight(.semibold)
                            Text(cons.category)
                                .font(.subheadline)
                        }
                    }
                }).navigationTitle("Favorites")
            }
        }
    }
}

struct favorites_Previews: PreviewProvider {
    @State static private var console: ConsoleDetails = ConsoleList.consoles.first!
    static var previews: some View {
        Favorites(con: $console) //Cannot convert value of type 'Binding<ConsoleDetails>' to expected argument type 'Binding<[ConsoleDetails]>'
        
    }
}

Here's the Console Detail View with the favorites button near the end:

import SwiftUI

struct ConsoleDetailView: View {
    
    @Binding var con: ConsoleDetails
    
    var body: some View {
        VStack{
            HStack{
                Spacer()
                Image(con.imgName)
                    .resizable()
                    .scaledToFit()
                    .frame( height: 60)
                VStack(alignment: .leading) {
                    Text(con.consoleName)
                        .font(.title2)
                        .fontWeight(.semibold)
                    if con.category == "Game & Watch" {
                        Text(con.mostSoldGame)
                    }
                }
                Spacer()
            }
            List{
                if con.category == "Game & Watch"{
                    Text("Release Date: \(con.ReleaseDate)")
                    Text("Initial Price: $\(con.initialPrice)")
                    Text("Discontinuation Date: \(con.Discontinuation)")
                    Text("""
                        Estimated Price Today (07.2022):
                        \(con.estimatedPricedToday)
                        """)
                    Text("Game & Watch category: \(con.mostSoldGame)")
                    Text("Batteries: \(con.cables)")
                    Text("""
                        Processor:
                        \(con.processor)
                        """)
                    Text("""
                    Screen Size:
                    \(con.screenSize)
                    """)
                    if con.dims != ""{
                        Text("""
                        Dimensions:
                        \(con.dims)
                        """)
                    }
                    //
                }else{
                    Text("Release Date: \(con.ReleaseDate)")
                    if con.consoleName == "Famicom" {
                        Text(" Initial Price: \(con.initialPrice)")
                    } else if con.consoleName == "Super Famicom" {
                        Text(" Initial Price: \(con.initialPrice)")
                    } else {
                        Text(" Initial Price: $\(con.initialPrice)")
                    }
                    Text("Discontinuation Date: \(con.Discontinuation)")
                    Text("""
                         Estimated Price Today (07.2022):
                        \(con.estimatedPricedToday)
                        """)
                    Text("""
                        Best Selling Game:
                        \(con.mostSoldGame)
                        """)
                    Text("Ports: \(con.cables)")
                    Text("""
                        Processor:
                        \(con.processor)
                        """)
                    if con.screenSize != ""{
                        Text("""
                        Screen Size:
                        \(con.screenSize)
                        """)
                    }
                    if con.dims != ""{
                        Text("""
                        Dimensions:
                        \(con.dims)
                        """)
                    }
                    //heres the button
                    Button("Add to favorites") {
                        con.favorites.toggle()
                    }
                    
                    
                }
                
            }.listStyle(.grouped)
            
        }
        
    }
    
    
    
    struct ConsoleDetailView_Previews: PreviewProvider {
        @State static private var console: ConsoleDetails = ConsoleList.consoles.first!
         static var previews: some View {
             ConsoleDetailView(con: $console)
             
         }
    }

Here's the list of all consoles (but I put one to save space):

import Foundation
import SwiftUI

struct ConsoleDetails: Identifiable{
    let id = UUID()
    var imgName: String = ""
    var consoleName: String = ""
    var mostSoldGame: String = ""
    var initialPrice: String = ""
    var ReleaseDate: String = ""
    var Discontinuation: String = ""
    var category: String = ""
    var estimatedPricedToday: String = ""
    var cables: String = ""
    var processor: String = ""
    var screenSize: String = ""
    var dims: String = ""
    var favorites: Bool = false
    var discontinued: Bool = true
    var isHandheld: Bool = true
}


struct ConsoleList{
    //The consoles list but I'll only put one to save space
    static var categories = Dictionary(grouping: consoles, by: {$0.category } )
    static var consoles = [
        //Current Consoles
        ConsoleDetails(imgName: "NS_OG",
                       consoleName: "Nintendo Switch",
                       mostSoldGame: "Mario Kart 8 Deluxe",
                       initialPrice: "299.99",
                       ReleaseDate: "Mar 3, 2017",
                       Discontinuation: "Still Available",
                       category: "Hybrid",
                       estimatedPricedToday: "$200-250 used",
                       cables: "HDMI, USB Type-C, Micro SD card slot, 3x USB Ports",
                       processor: "Nvidia Tegra X1",
                       screenSize: """
                       Capacitive touch screen
                       6.2 inch LCD Screen
                       1280 x 720
                       """,
                       dims: """
                          W: 9.40\"
                          H: 4.01\"
                          D: 0.55\"
                          """,
                       favorites: false,
                       discontinued: false,
                       isHandheld: true),
Techtronis
  • 35
  • 6
  • Welcome to SO - Please take the [tour](https://stackoverflow.com/tour) and read [How to Ask](https://stackoverflow.com/help/how-to-ask) to improve, edit and format your questions. Without a [Minimal Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example) it is impossible to help you troubleshoot. – lorem ipsum Jul 31 '22 at 19:15
  • @burnsi I want the console in which I press the "Add to favorites button" to save it in the favorites view. Which means I need to change a favourites bool in the console list to true. When I press on the same button again I want it to be removed from the favorites view – Techtronis Jul 31 '22 at 19:28

1 Answers1

0

If you want your DetailView to propagate changes to its parent you need to use a Binding. Change the con var in your Favorites to be a @State and use following syntax in your List:

struct Favorites: View {
    //Create the state
    @State var con: [ConsoleDetails]
    
    var body: some View {
        // list of consoles
        // Iterate over the states. Note the $ symbol it´s important
        List($con){ $cons in
            if cons.favorites {
                //Hand the binding to the subview
                NavigationLink(destination: ConsoleDetailView(con: $cons), label:{
                    Image(cons.imgName)
                        .resizable()
                        .scaledToFit()
                        .frame(width: 50, height: 50)
                        .cornerRadius(4)
                        .padding(.vertical, 4)
                    
                    VStack (alignment: .leading){
                        if cons.category == "Game & Watch" {
                            Text(cons.consoleName)
                                .fontWeight(.semibold)
                            Text(cons.mostSoldGame)
                                .font(.subheadline)
                        }else{
                            Text(cons.consoleName)
                                .fontWeight(.semibold)
                            Text(cons.category)
                                .font(.subheadline)
                        }
                    }
                }).navigationTitle("Favorites")
                    
            }
        }
    }
}

and in your ConsoleDetailView change the var to be a @Binding:

struct ConsoleDetailView: View {
    
    @Binding var con: ConsoleDetails
    
    var body: some View {
        VStack{
            HStack{
                Spacer()
                Image(con.imgName)
                    .resizable()
                    .scaledToFit()
                    .frame( height: 60)
                VStack(alignment: .leading) {
                    Text(con.consoleName)
                        .font(.title2)
                        .fontWeight(.semibold)
                    if con.category == "Game & Watch" {
                        Text(con.mostSoldGame)
                    }
                }
                Spacer()
            }
            List{
                if con.category == "Game & Watch"{
                    Text("Release Date: \(con.ReleaseDate)")
                    Text("Initial Price: $\(con.initialPrice)")
                    Text("Discontinuation Date: \(con.Discontinuation)")
                    Text("""
                        Estimated Price Today (07.2022):
                        \(con.estimatedPricedToday)
                        """)
                    Text("Game & Watch category: \(con.mostSoldGame)")
                    Text("Batteries: \(con.cables)")
                    Text("""
                        Processor:
                        \(con.processor)
                        """)
                    Text("""
                    Screen Size:
                    \(con.screenSize)
                    """)
                    if con.dims != ""{
                        Text("""
                        Dimensions:
                        \(con.dims)
                        """)
                    }
                    //
                }else{
                    Text("Release Date: \(con.ReleaseDate)")
                    if con.consoleName == "Famicom" {
                        Text(" Initial Price: \(con.initialPrice)")
                    } else if con.consoleName == "Super Famicom" {
                        Text(" Initial Price: \(con.initialPrice)")
                    } else {
                        Text(" Initial Price: $\(con.initialPrice)")
                    }
                    Text("Discontinuation Date: \(con.Discontinuation)")
                    Text("""
                         Estimated Price Today (07.2022):
                        \(con.estimatedPricedToday)
                        """)
                    Text("""
                        Best Selling Game:
                        \(con.mostSoldGame)
                        """)
                    Text("Ports: \(con.cables)")
                    Text("""
                        Processor:
                        \(con.processor)
                        """)
                    if con.screenSize != ""{
                        Text("""
                        Screen Size:
                        \(con.screenSize)
                        """)
                    }
                    if con.dims != ""{
                        Text("""
                        Dimensions:
                        \(con.dims)
                        """)
                    }
                    //heres the button
                    Button("Add to favorites") {
                        con.favorites.toggle()
                    }
                    
                    
                }
                
            }.listStyle(.grouped)
            
        }
        
    }
    
}

Edit: Regarding your error message in the preview. As you now have to provide a Binding<ConsoleDetails> in the initializer you can either use a @State var:

struct ConsoleDetailView_Previews: PreviewProvider {
    @State static private var console: ConsoleDetails = ConsoleList.consoles.first!
    
    static var previews: some View {
        ConsoleDetailView(con: $console)
        
    }
}

or use a constant Binding:

ConsoleDetailView(con: .constant(ConsoleList.consoles.first!))
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
burnsi
  • 6,194
  • 13
  • 17
  • 27
  • I get an error in the struct ConsoleDetailView_Previews, check the edit – Techtronis Jul 31 '22 at 20:26
  • @Techtronis please see my updated answer. – burnsi Jul 31 '22 at 22:59
  • The button still doesn't do anything – Techtronis Aug 01 '22 at 10:33
  • @Techtronis this is working for me. In absence of a [mre] I used only the `Favorites` view. Try copying the code I provided into a new project and try it out. It will work. If it doesnt´t work for you the issue is probably somewhere in code you didn´t provide. – burnsi Aug 01 '22 at 10:40
  • @Techtronis After reviewing your `MainMenu` it seems your `con` var should be a `@State` and the `con` var in `Favorites` should be a `@Binding`, just a guess. – burnsi Aug 01 '22 at 10:45
  • I've done the modifications but I get an error in `Favorites` Check the edit – Techtronis Aug 01 '22 at 13:58
  • @Techtronis I know it can be hard as someone who is new to programming. But you really need to step it up a bit. I allready gave you the knowledge of how to solve this problem. The only thing you now have to comprehende is the difference between `[ConsoleDetails]` and `ConsoleDetails`. But I am under the impression that this is a task I should let you solve on your own. – burnsi Aug 01 '22 at 15:33