Reputation: 1623
Building my first SwiftUI app, and have some basic knowledge of Swift. So a bit much to chew but I am enjoying learning.
I have a Form with many toggles saving/restoring from core data in my swift app. Works well but the interface is cumbersome with all the toggles.
Instead I want to make an HStack
of tappable labels that will be selected / unselected instead. Then when you submit it will map the selected Text objects to the existing State variables I have OR? save an array of selected strings to core data (for restoring later?).
In either case my code for this has been cobbled from a todo list tutorial plus some nice HStack
examples I have put in my form. They select/deselect nicely but I do not know how to save their state like I did the toggle switches.
I will paste what I think is relevant code and remove the rest.
@State var selectedItems: [String] = []
@State private var hadSugar = false
@State private var hadGluten = false
@State private var hadDairy = false
let dayvariablesText = [
"Sugar",
"Gluten",
"Dairy"
]
// section 1 works fine
Section {
VStack {
Section(header: Text("Actions")) {
Toggle("Sugar", isOn: $hadSugar)
Toggle("Gluten", isOn: $hadGluten)
Toggle("Dairy", isOn: $hadDairy)
}
}
}
// section 2 trying this
ScrollView(.horizontal) {
LazyHGrid(rows: rows) {
ForEach(0..<dayvariablesText.count, id: \.self) { item in
GridColumn(item: dayvariablesText[item], items: $selectedItems)
}
}
}.frame(width: 400, height: 100, alignment: .topLeading)
// save
Button("Submit") {
DataController().addMood(sugar: hadSugar, gluten: hadGluten, dairy: hadDairy, context: managedObjContext)
dismiss()
}
This works fine with the toggles shown above - how to do this when selecting gridItems in the next section for example?
Upvotes: 1
Views: 55
Reputation: 7744
I think you need to remodel your code. Having multiple sources of truth like in your example (with the vars and the array for the naming) is a bad practice and will hurt you in the long run.
Consider this solution. As there is a lot missing in your question it´s more general. So you need to implement it to fit your needs. But it should get you in the right direction.
//Create an enum to define your items
// naming needs some improvement :)
enum MakroType: String, CaseIterable{
case sugar = "Sugar", gluten = "Gluten", dairy = "Dairy"
}
//This struct will hold types you defined earlier
// including the bool indicating if hasEaten
struct Makro: Identifiable{
var id: MakroType {
makro
}
var makro: MakroType
var hasEaten: Bool
}
// The viewmodel will help you store and load the data
class Viewmodel: ObservableObject{
//define and create the array to hold the Makro structs
@Published var makros: [Makro] = []
init(){
// load the data either here or in the view
// when it appears
loadCoreData()
}
func loadCoreData(){
//load items
// ..... code here
// if no items assign default ones
if makros.isEmpty {
makros = MakroType.allCases.map{
Makro(makro: $0, hasEaten: false)
}
}
}
// needs to be implemented
func saveCoreData(){
print(makros)
}
}
struct ContentView: View {
// Create an instance of the Viewmodel here
@StateObject private var viewmodel: Viewmodel = Viewmodel()
var body: some View {
VStack{
ScrollView(.horizontal) {
LazyHStack {
// Iterate over the items themselves and not over the indices
// with the $ in front you can pass a binding on to the ChildView
ForEach($viewmodel.makros) { $makro in
SubView(makro: $makro)
}
}
}.frame(width: 400, height: 100, alignment: .topLeading)
Spacer()
Button("Save"){
viewmodel.saveCoreData()
}.padding()
}
.padding()
}
}
struct SubView: View{
// Hold the binding to the Makro here
@Binding var makro: Makro
var body: some View{
//Toggle to change the hasEaten Bool
//this will reflect through the Binding into the Viewmodel
Toggle(makro.makro.rawValue, isOn: $makro.hasEaten)
}
}
Upvotes: 1