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