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),
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
//Itterate 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!))
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.