Home > Software design >  SwiftUI- passing @State variables to multiple views trouble
SwiftUI- passing @State variables to multiple views trouble

Time:09-17

Im trying to make an app similar to iphones reminders app in terms of UI. I have a view where I have a list that I can add and delete items from, I also have a view for adding an item that allows me to name the item and select some options, and I have another view for when an item in the list is selected and I want to be able to show the name and options I made but it doesn't display. The code for the list view

struct DotView: View {

enum ActiveSheet: String, Identifiable {
    case SwiftUIView, EditView
    var id: String {
        return self.rawValue
    }
}

@EnvironmentObject var listViewModel: ListViewModel
@AppStorage("dotApiKey") var selectedDotApi: String = ""
@State var dotName:String = ""
@State var dotNumber:String = ""
@State var selection:String = ""
@State var triggerSelection:String = ""
@State var searchText:String = ""
@State var plugUsername:String = ""
@State var plugPassword:String = ""
@State var toggleNotification:Bool
@State var activeSheet : ActiveSheet? = nil
    
@Binding var show : Bool

var body: some View {
        ZStack{
            HStack {
                EditButton()
                    .padding(.leading)
                Spacer()
                Button(action: {
                    self.activeSheet = .SwiftUIView
                }, label: {
                    Text("Add")
                })
                    .padding(.trailing)
            }

            ZStack {
                List {
                    ForEach(listViewModel.dotitems, id:\.dotId){ dotitem in
                        Button(action: { self.activeSheet = .EditView }, label: {
                            DotItemListView(dotitem: dotitem)
                        })
                    }
                    .onDelete(perform: listViewModel.deleteItem)
                    .onMove(perform: listViewModel.moveItem)
                    .listRowBackground(Color("textBG"))
                    
                }.listStyle(PlainListStyle())
                .background(Color("textBG"))
                .frame(height: 300)
                .cornerRadius(10)
                .padding(.horizontal)                    
           }
       }
   .sheet(item: $activeSheet){ sheet in
        switch sheet {
        case .SwiftUIView:
            SwiftUIView(dotName: dotName, dotNumber: dotNumber, selection: selection, triggerSelection: triggerSelection, searchText: searchText, plugUsername: plugUsername, plugPassword: plugPassword, show: $show)
        case .EditView:
            EditView(show:$show)
        }
    }

When I add an item to the list it shows this in each row

struct DotItemListView:View {
let dotitem: DotItem

var body: some View{
    HStack {
        Text(dotitem.dotName)
        Spacer()
        Text(dotitem.selection)
        Spacer()
        Text(dotitem.dotNumber)
        Spacer()
    }
}

}

This is how I'm adding each item to the list

struct DotItem:Equatable, Codable{
var dotId = UUID().uuidString
let dotName:String
let dotNumber:String
let selection:String
}

class ListViewModel: ObservableObject {

@Published var dotitems: [DotItem] = [] {
    didSet {
        saveItem()
    }
}
let dotitemsKey: String = "dotitems_list"

init() {
    getDotItems()
}

func getDotItems() {
    guard
        let data = UserDefaults.standard.data(forKey: dotitemsKey),
        let savedDotItems = try? JSONDecoder().decode([DotItem].self, from: data)
    else { return }
    
    self.dotitems = savedDotItems
}

func deleteItem(indexSet: IndexSet){
    dotitems.remove(atOffsets: indexSet)
}

func moveItem(from: IndexSet, to: Int){
    dotitems.move(fromOffsets: from, toOffset: to)
}

func addItem(dotName: String, dotNumber: String, selection: String){
    let newItem = DotItem(dotName: dotName, dotNumber: dotNumber, selection: selection)
    dotitems.append(newItem)
    
    print(newItem)
}

func saveItem() {
    if let encodedData = try? JSONEncoder().encode(dotitems) {
        UserDefaults.standard.set(encodedData, forKey: dotitemsKey)
    }
}
}

This is the view that I'm entering the data for each item

struct SwiftUIView: View {

@Environment(\.presentationMode) var presentationMode
@EnvironmentObject var listViewModel: ListViewModel
@AppStorage("dotApiKey") var selectedDotApi: String = ""
@State var dotName:String
@State var dotNumber:String
@State var selection:String
@State var triggerSelection:String
@State var selectedColor = Color.black
@State var searchText:String
@State var plugUsername:String
@State var plugPassword:String
@ObservedObject var vm = getDeviceNames()
@State var triggerDot:Bool = false
@State var toggleOnOff:Bool = false
@State var toggleLightColor:Bool = false
@State var isSearching:Bool = false

@StateObject var camera = CameraModel()

@Binding var show : Bool

var body: some View {
    NavigationView {
                    Form {
                        Section(header: Text("Info")) {
                            TextField("Name", text: $dotName)
                            TextField("Number", text: $dotNumber)
                            Picker(selection: $selection, label: Text("Discover Plug")) {
                                ForEach(vm.dataSet, id:\.self) { item in
                                    Text(item.Device).tag(item.Device)
                                }
                            }
                            Toggle(isOn: $isSearching, label: {
                                Text("Have smart plugs?")
                            })
                            if isSearching {
                                HStack{
                                    Text("Casa")
                                    Spacer()
                                    Button(action: {sendPlugDict()}, label: {
                                        Text("Login")
                                    })
                                }
                                TextField("Username", text: $plugUsername)
                                TextField("Password", text: $plugPassword)
                                
                            }
                        }
                        Section {
                            Toggle(isOn: $toggleOnOff, label: {
                                Text("On/Off")
                            })
                        }
                        Section {
                            Toggle(isOn: $toggleLightColor, label: {
                                Text("Light Color")
                            })
                            if toggleLightColor {
                                ColorPicker("Choose Light Color", selection: $selectedColor)
                            }
                        }
                        Section {
                            if listViewModel.dotitems.isEmpty == false {
                                Toggle(isOn: $triggerDot, label: {
                                    Text("Add a DOT to trigger")
                                })
                                if triggerDot {
                                    Picker(selection: $triggerSelection, label: Text("Select DOT")) {
                                        ForEach(listViewModel.dotitems, id:\.dotId){ dotitem in
                                            DotItemListView(dotitem: dotitem)
                                        }
                                    }
                                }
                            }
                        }
                    }
                    .navigationBarTitle("")
                    .navigationBarHidden(true)
                }
}
}

and this is the view that I'm trying to show the data when any list item is selected which is basically the same as above except in a few places

struct EditView: View {
@Environment(\.presentationMode) var presentationMode
@EnvironmentObject var listViewModel: ListViewModel
@ObservedObject var vm = getDeviceNames()
@State var dataSet = [Result]()
@State var dotName:String = ""
@State var dotNumber:String = ""
@State var selection:String = ""
@State var triggerSelection:String = ""
@State var selectedColor = Color.black
@State var searchText:String = ""
@State var plugUsername:String = ""
@State var plugPassword:String = ""
@State var triggerDot:Bool = false
@State var toggleOnOff:Bool = false
@State var toggleLightColor:Bool = false
@State var isSearching:Bool = false

@Binding var show : Bool

var body: some View {
    NavigationView {
                    Form {
                        Section(header: Text("Info")) { 
                            TextField(dotName, text: $dotName)
                            TextField(dotNumber, text: $dotNumber)
                            Picker(selection: $selection, label: Text("Discorver Plug")) {
                                ForEach(dataSet, id:\.self) { item in
                                    Text(item.Device).tag(item.Device)
                                }
                            }
                            Toggle(isOn: $isSearching, label: {
                                Text("Have smart plugs?")
                            })
                            if isSearching {
                                HStack{
                                    Text("Casa")
                                    Spacer()
                                    Button(action: {
                                            SwiftUIView(dotName: dotName, dotNumber: dotNumber, selection: selection, triggerSelection: triggerSelection, searchText: searchText, plugUsername: plugUsername, plugPassword: plugPassword, show: $show).sendPlugDict()}, label: {
                                        Text("Login")
                                    })
                                }
                                TextField("Username", text: $plugUsername)
                                TextField("Password", text: $plugPassword)
                                
                            }
                        }
                        Section {
                            Toggle(isOn: $toggleOnOff, label: {
                                Text("On/Off")
                            })
                        }
                        Section {
                            Toggle(isOn: $toggleLightColor, label: {
                                Text("Light Color")
                            })
                            if toggleLightColor {
                                ColorPicker("Choose Light Color", selection: $selectedColor)
                            }
                        }
                        Section {
                            if listViewModel.dotitems.isEmpty == false {
                                Toggle(isOn: $triggerDot, label: {
                                    Text("Add a DOT to trigger")
                                })
                                if triggerDot {
                                    Picker(selection: $triggerSelection, label: Text("Select DOT")) {
                                        ForEach(listViewModel.dotitems, id:\.dotId){ dotitem in
                                            DotItemListView(dotitem: dotitem)
                                        }
                                    }
                                }
                            }
                        }
                    }
                    .navigationBarTitle("")
                    .navigationBarHidden(true)
                }

}
}

I know this is a lot to go through but any help would be greatly appreciated

CodePudding user response:

use @Binding on the view that you want to pass state to .

View1 :

@State var a = true 

View2(a: $a)

View2 :

@Binding var a : Bool

for passing data globally use @EnvoirmentObject :

example :

@main
struct YourApp: App {

    @StateObject var session = Session()
    
    var body: some Scene {
        WindowGroup {
            SplashView()
                .environmentObject(session)
        }
    }
}

Session :

class Session: ObservableObject {

    /// user signin state
    @Published  var isSignedIn : Bool = false
}

View1 ,View2 , View3 .... :

struct SplashView: View {
    
    @EnvironmentObject var session : Session
    
    var body: some View {
        VStack{
            SignInView()
                .opacity(session.isSignedIn ? 0:1)
        }
        .background(Color.background.ignoresSafeArea())
    }
}
  • Related