Persistent data when editing an existing array in swiftui - arrays

I am trying to figure out how to make two things work together. The first is persistent data using userdefaults, which I have figured out by using #Published and #Observable, then using JSONencoder/decoder to get that data to save even when the app is closed and reopened. The second I have also recently learned via this previous question < https://stackoverflow.com/questions/62106227/best-way-to-update-edit-an-array-element-in-swiftui/67752060#67752060>
My issue is combining the two. I can’t seem to figure out how to take the below code with predefined array of data and make the changes persist using UserDefaults. Any help would be greatly appreciated. Maybe I’m approaching this incorrectly.
Code for persistent data via UserDefaults
import SwiftUI
struct CharacterModel: Identifiable, Codable {
var id = UUID()
var name: String
var level: Int
}
class CharacterViewModel: ObservableObject {
#Published var characters = [CharacterModel]() {
// Write data back to Model
didSet {
let encoder = JSONEncoder()
if let encoded = try?
encoder.encode(characters) {
UserDefaults.standard.set(encoded, forKey: "Characters")
}
}
}
init() {
if let characters = UserDefaults.standard.data(forKey: "Characters") {
let decoder = JSONDecoder()
if let decoded = try?
decoder.decode([CharacterModel].self, from: characters) {
self.characters = decoded
return
}
}
self.characters = []
}
}
struct DetailView: View {
#Environment(\.presentationMode) var presentationMode
#ObservedObject var characterVM: CharacterViewModel
#State private var name = ""
#State private var level = ""
var body: some View {
NavigationView {
Form {
TextField("Name", text: $name)
TextField("Level", text: $level)
.keyboardType(.numberPad)
}
.navigationBarTitle("Add Level")
.navigationBarItems(trailing: Button("Save") {
if let actualLevel = Int(self.level) {
let character = CharacterModel(name: self.name, level: actualLevel)
self.characterVM.characters.append(character)
self.presentationMode.wrappedValue.dismiss()
}
})
}
}
}
struct DetailView_Previews: PreviewProvider {
static var previews: some View {
DetailView(characterVM: CharacterViewModel())
}
}
struct Home: View {
#State private var showingAddCharacter = false
#State var selectedCharacter : CharacterModel!
#ObservedObject var characterVM = CharacterViewModel()
var body: some View {
NavigationView {
List {
ForEach(characterVM.characters) { char in
HStack {
VStack(alignment: .leading) {
Text(char.name)
.font(.headline)
}
Spacer()
Text("\(char.level)")
}
}
.onDelete(perform: removeItems)
}
.navigationBarTitle("Characters")
.navigationBarItems(trailing:
Button(action: {
self.showingAddCharacter = true
}) {
Image(systemName: "plus")
}
)
.sheet(isPresented: $showingAddCharacter) {
DetailView(characterVM: self.characterVM)
}
}
}
func removeItems(at offsets: IndexSet) {
characterVM.characters.remove(atOffsets: offsets)
}
}
struct Home_Previews: PreviewProvider {
static var previews: some View {
Home()
}
}
Editing a predefined array
class Training: ObservableObject, Identifiable {
let id: String
#Published var trainingName: String
#Published var isRequired: Bool
init(id: String, trainingName: String, isRequired: Bool) {
self.id = id
self.trainingName = trainingName
self.isRequired = isRequired
}
}
class GetTrainings: ObservableObject {
#Published var items = [Training]()
init() {
self.items = [
Training(id: "ttt1", trainingName: "Safety", isRequired: true),
Training(id: "ttt2", trainingName: "Administrative", isRequired: false),
Training(id: "ttt3", trainingName: "Computer", isRequired: true),
Training(id: "ttt4", trainingName: "People", isRequired: true),
Training(id: "ttt5", trainingName: "Managerial", isRequired: true),
]
}
}
struct TrainingList: View {
#ObservedObject var trainings = GetTrainings()
var body: some View {
NavigationView {
VStack {
List {
ForEach(trainings.items) { training in
HStack {
NavigationLink(destination: TrainingView(training: training)) {
Text("\(training.trainingName)")
}
}
}
}
}.navigationBarTitle("Training List")
.onAppear {
self.trainings.objectWillChange.send() // refresh
}
}
}
}
struct TrainingView: View {
#ObservedObject var training: Training
var body: some View {
VStack {
Text("\(training.trainingName)").font(.body)
Text("\(training.isRequired == true ? "Required Training" : "Training Not Required")")
HStack {
NavigationLink(destination: EditTraining(training: training)) {
Text("Edit Training Details")
}
}
}.navigationBarTitle("\(training.trainingName) Page", displayMode: .inline)
}
}
struct EditTraining: View {
#ObservedObject var training: Training
#State private var newName: String
#State private var isRequiredTraining: Bool
init(training: Training) {
self.training = training
self._newName = State(initialValue: training.trainingName)
self._isRequiredTraining = State(initialValue: training.isRequired)
}
private func submitData() {
let newName = self.newName
let newBoolVal = self.isRequiredTraining
print("Firebase Sync Id is :\(training.id) Text: \(newName) and Bool: \(newBoolVal)")
self.training.trainingName = newName
self.training.isRequired = newBoolVal
}
var body: some View {
VStack {
Form {
Section (header: Text("Edit")) {
Text("\(training.trainingName)")
/* TextField should Populate With passed In Training Name Here*/
TextField("New Name", text: self.$newName)
Toggle(isOn: self.$isRequiredTraining) {
Text("Is Required")
}
}
Section {
Button(action: {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder),
to:nil, from:nil, for:nil)
self.submitData()
}) {
Text("Submit")
}
}
}
}.navigationBarTitle("Edit Training Page", displayMode: .inline)
}
}

Related

DetailView only calls on first item in array when using LazyVGrid

I had a list that I could add items to and when I click on them it opened up the correct detail view. I recently swapped the list out for LazyVGrid but when I click on a grid item, the detail view only calls on the first item in the array.
This is the list view:
import SwiftUI
struct ListView: View {
#EnvironmentObject var listViewModel: ListViewModel
#State var addItemView = false
#State var detailView = false
let columns = [GridItem(.flexible()), GridItem(.flexible())]
var body: some View {
NavigationView {
ZStack {
ZStack {
Color(.white)
}
VStack {
Button(action: {
self.addItemView = true
}, label: {
Text("Add")
}).sheet(isPresented: $addItemView, content: {
AddItemView()
})
LazyVGrid(columns: columns, spacing: 20) { // <-- the only line i comment when using list
// List {
ForEach(listViewModel.item, id:\.id){ item in
Button(action: {
self.detailView = true
}, label: {
ListRowView(item: item)
}).sheet(isPresented: $detailView, content: {
DetailView(item: item)
})
}
// .onDelete(perform: listViewModel.deleteItem)
// .onMove(perform: listViewModel.moveItem)
// .listRowBackground(Color.blue)
}
// .listStyle(PlainListStyle())
Spacer()
}
}
}
}
}
struct ListRowView: View { // <-- using this as grid item
#State var item:Item
var body: some View{
VStack {
Text(item.name).foregroundColor(.white)
}.frame(width: 150, height: 150, alignment: .center)
.background(Color.blue)
.cornerRadius(10)
}
}
The add item and detail view:
struct AddItemView: View {
#Environment(\.presentationMode) var presentationMode
#EnvironmentObject var listViewModel: ListViewModel
#State var id = UUID()
#State var name = ""
var body: some View {
TextField("Enter Name", text: $name)
Button(action: {
addItem()
}, label: {
Text("Done")
})
}
func addItem() {
listViewModel.addItem(id: id.uuidString, name: name)
presentationMode.wrappedValue.dismiss()
}
}
struct DetailView: View {
#State var item:Item
var body: some View {
Text(item.name)
}
}
And this is how im adding each item:
import Foundation
struct Item: Hashable, Codable, Equatable {
var id:String
var name: String
}
class ListViewModel: ObservableObject {
#Published var item: [Item] = [] {
didSet {
saveItem()
}
}
let itemsKey: String = "items_key"
init() {
getItems()
}
func getItems() {
guard
let data = UserDefaults.standard.data(forKey: itemsKey),
let savedItems = try? JSONDecoder().decode([Item].self, from: data)
else { return }
self.item = savedItems
}
func deleteItem(indexSet: IndexSet){
item.remove(atOffsets: indexSet)
}
func moveItem(from: IndexSet, to: Int){
item.move(fromOffsets: from, toOffset: to)
}
func addItem(id: String, name: String){
let newItem = Item(id: id, name: name)
item.append(newItem)
print(newItem)
}
func saveItem() {
if let encodedData = try? JSONEncoder().encode(item) {
UserDefaults.standard.set(encodedData, forKey: itemsKey)
}
}
}
I'm not sure why the LazyVGrid is only calling on the first item, any help would be appreciated
The problem is here:
#State var detailView = false /// will only work for 1 sheet, not multiple...
...
ForEach(listViewModel.item, id:\.id){ item in
Button(action: {
self.detailView = true
}, label: {
ListRowView(item: item)
}).sheet(isPresented: $detailView, content: { /// not good to have `sheet` inside ForEach
DetailView(item: item)
})
}
In each iteration of the ForEach, you have a sheet. That's a lot of sheets... and when detailView is set to true, all of them will try to present. By chance, the first once gets presented.
Instead, you'll need to use sheet(item:onDismiss:content:). This alternate version of sheet is specifically made for your purpose — when you have an array of Items and want to present a sheet for that specific Item.
First, you'll need to make Item conform to Identifiable.
/// here!
struct Item: Hashable, Codable, Equatable, Identifiable {
Then, replace the old trigger, #State var detailView = false, with #State var selectedDetailItem: Item?. Also, make sure to move the sheet outside of the ForEach, so it doesn't repeat. Now, the sheet will only present when selectedDetailItem is not nil.
struct ListView: View {
#EnvironmentObject var listViewModel: ListViewModel
#State var addItemView = false
#State var selectedDetailItem: Item? /// here!
let columns = [GridItem(.flexible()), GridItem(.flexible())]
var body: some View {
NavigationView {
ZStack {
ZStack {
Color(.white)
}
VStack {
Button(action: {
self.addItemView = true
}, label: {
Text("Add")
}).sheet(isPresented: $addItemView, content: {
AddItemView()
})
LazyVGrid(columns: columns, spacing: 20) {
ForEach(listViewModel.item, id:\.id){ item in
Button(action: {
self.selectedDetailItem = item /// set the selected item
}, label: {
ListRowView(item: item)
})
}
}
Spacer()
}
}
} /// present the sheet here
.sheet(item: $selectedDetailItem) { selectedItem in
DetailView(item: selectedItem)
}
}
}
Result:

Problems with switching array elements

I have a class that has a calculated property, which is an array consisting of instances of the structure.
struct Team: Identifiable, Codable, Hashable {
var id = UUID()
var name : String
}
class TeamRow : ObservableObject {
#Published var teamsArray : [Team] = [] {
didSet {
let encoder = JSONEncoder()
if let encoded = try? encoder.encode(teamsArray) {
UserDefaults.standard.setValue(encoded, forKey: "Teams")
}
}
}
init() {
if let teams = UserDefaults.standard.data(forKey: "Teams") {
let decoder = JSONDecoder()
if let decoded = try? decoder.decode([Team].self, from: teams) {
self.teamsArray = decoded
return
}
}
}
}
Also, I have a view, with the ability to add elements(teams) to this array using a sheet.
struct PlayersRow: View {
#ObservedObject var teams = TeamRow()
#State private var team = ""
#State private var showTeamAddSheet = false
var body: some View {
Form {
ForEach(teams.teamsArray) { team in
Text(team.name)
.font(.system(size: 20))
.padding(.horizontal, 110)
.padding(.vertical, 10)
}
}
.navigationBarTitle("Teams")
.navigationBarItems(trailing: Button(action: {
self.showTeamAddSheet = true
}) {
Image(systemName: "plus")
.foregroundColor(.black)
.font(.system(size: 30))
})
.sheet(isPresented: $showTeamAddSheet) {
AddPlayerView(teams: self.teams)
}
}
}
This is a sheet view.
struct AddPlayerView: View {
#Environment(\.presentationMode) var presentationMode
#ObservedObject var teams : TeamRow
#State private var team = ""
var body: some View {
NavigationView {
Form {
TextField("Add new team", text: $team)
}
.navigationBarItems(trailing: Button(action: {
let newTeam = Team(name: self.team)
self.teams.teamsArray.append(newTeam)
self.presentationMode.wrappedValue.dismiss()
}, label: {
Text("Сохранить")
.font(.custom("coolJazz", size: 20))
.foregroundColor(.black)
}))
.navigationBarTitle("Add Team")
}
}
}
And I have a view where I need to output the array elements one by one, using a button, clicked on the button, the view screen displayed 0 element, clicked on the button, displayed first element, etc.
struct GameView: View {
#ObservedObject var teams = TeamRow()
//#State var index = 0
var body: some View {
VStack(spacing: 40) {
//Text(teams.teamsArray[index]) this isn't worked, return an error: Initializer 'init(_:)' requires that 'Team' conform to 'StringProtocol'
Button(action: {
}) {
Text("press it")
}
}
}
}
struct GameView_Previews: PreviewProvider {
static var previews: some View {
GameView().environmentObject(TeamRow())
}
}
if the array is set initially in the class, I have no problem displaying the elements on the screen by increasing the index, but how to solve this problem I do not know...
Can some one explain newbie?
Change your GameView Code to following:
struct GameView: View {
#ObservedObject var teams = TeamRow()
#State var index = 0
var body: some View {
VStack(spacing: 40) {
if teams.teamsArray.count > index {
Text(teams.teamsArray[index].name)
}
Button(action: {
index += 1
}) {
Text("press it")
}
}
}
}

How to sort a array with localized String in SwiftUI

Hi my problem is that i want to sort an array of objects by the object's title property. When i change the title's type from String to LocalizedStringKey i get an error. Is there a way to sort the correct translated string behind the localizedStringKey.
struct Item: Codable,Comparable, Hashable, Identifiable {
static func < (lhs: Item, rhs: Item) -> Bool {
return lhs.title < rhs.title
}
var id: Int
let image: String
let color: String
// title should be LocalizedStringKey
let title: String
}
......
#State private var sortedDown = false
var filteredItems: [Item] {
var sortedItems: [Item]
let filteredItems = modelData.items.filter { item in
(!showFavoritesOnly || item.isFavorite)
}
if sortedDown {
sortedItems = filteredItems.sorted(by: { (item1, item2) -> Bool in
return item1.title > item2.title
})
} else {
sortedItems = filteredItems.sorted(by: { (item1, item2) -> Bool in
return item1.title < item2.title
})
}
return sortedItems
}
var body: some View {
NavigationView {
List {
Toggle(isOn: $showFavoritesOnly, label: {
Text("showFavorites")
})
ForEach(filteredItems) { (item) in
.....
``
Don't forget to provide a minimal reproducible example when you ask questions. You will get better and more focused answers. Below is one way you can do it.
class LocalizeTitleViewModel: ObservableObject {
#Published var items: [SampleItem] = [SampleItem(title: "orange", isFavorite: true), SampleItem(title: "pink", isFavorite: false), SampleItem(title: "red", isFavorite: true)]
#Published var sortedDown = false
#Published var showFavoritesOnly = false
var filteredItems: [SampleItem] {
var sortedItems: [SampleItem]
let filteredItems = items.filter { item in
(!showFavoritesOnly || item.isFavorite)
}
if sortedDown {
sortedItems = filteredItems.sorted(by: { (item1, item2) -> Bool in
return item1.localizedTitle > item2.localizedTitle
})
} else {
sortedItems = filteredItems.sorted(by: { (item1, item2) -> Bool in
return item1.localizedTitle < item2.localizedTitle
})
}
return sortedItems
}
}
struct LocalizeTitle: View {
#StateObject var modelData: LocalizeTitleViewModel = LocalizeTitleViewModel()
var body: some View {
NavigationView {
List {
Toggle(isOn: $modelData.showFavoritesOnly, label: {
Text("showFavorites")
})
ForEach(modelData.filteredItems) { (item) in
Text(item.localizedTitle)
}
}
}
}
}
struct SampleItem: Identifiable {
var id: UUID = UUID()
let title: String
var isFavorite: Bool
var localizedTitle: String{
get{
//You need a Localizable.strings file in your project that contains all your `title`
return NSLocalizedString(self.title, comment: "Sample Item title")
}
}
}
struct LocalizeTitle_Previews: PreviewProvider {
static var previews: some View {
LocalizeTitle()
}
}
LocalizedStringKey does not conform to the Comparable protocol, so you cannot use the < or > operators on it.
You can either just use a regular String, or use custom comparison logic.

SwiftUI how to update a view when the object in a published array is updated and not the array itself

The following is a contrived example of the problem I am facing.
Situation:
I have a class PersonStore that stores class Person in an array and is Published.
The class Person creates a LegalAge model for each Person and the model is Published
Problem:
When an update to the persons age in LegalAge is performed the view for PersonStore is not updated.
I'm guessing that the reason there is no change is because the change occurs to the object within the array and not to the array itself. How do I overcome this?
import SwiftUI
enum LegalAge: Int, CaseIterable {
case UK = 18
case USA = 21
var name: String { "\(self)" }
}
struct ContentView: View {
var body: some View {
MainView()
}
}
/// Displays a list of people and their ages
struct MainView: View {
#ObservedObject var personStore = PersonStore()
#State private var addPerson = false
var body: some View {
NavigationView {
List {
ForEach(personStore.store) { person in
NavigationLink(destination: ShowAge(person: person)) {
HStack {
Text("\(person.name)")
Text("\(person.model.personsAge)")
Text("\(person.model.country)")
Image(systemName: (person.model.personsAge >= person.model.drinkingAge) ? "checkmark.shield.fill" : "xmark.shield.fill").foregroundColor((person.model.personsAge >= person.model.drinkingAge) ? Color.green : Color.red)
}
}
}
}
.navigationBarTitle(Text("Can I Drink?"))
.navigationBarItems(leading: Button(action: { addPerson = true }) { Image(systemName: "plus") }
)
}
/// Open a sheet to create a new person.
.sheet(isPresented: $addPerson) { AddPerson().environmentObject(personStore) }
}
}
/// A Person store to hold all the people.
class PersonStore: ObservableObject {
#Published var store = [Person]()
func addPerson(person: Person) {
store.append(person)
}
func getPerson(name: String) -> Person? {
if let nameAtIndex = store.firstIndex(where: {$0.name == name}) {
return store[nameAtIndex]
}
return nil
}
func insertPerson(person: Person) {
store.append(person)
}
}
/// Form to allow adding people.
struct AddPerson: View {
#EnvironmentObject var personStore: PersonStore
#State private var name: String = ""
#State private var age: String = ""
#State private var country: LegalAge = LegalAge.UK
var body: some View {
NavigationView {
Form {
TextField("Name", text: $name)
TextField("Age", text: $age)
Picker(selection: $country, label: Text("Country")) {
ForEach(LegalAge.allCases, id: \.self) { c in
Text("\(c.name)").tag(c)
}
}
Section {
Button(action: { personStore.addPerson(person: Person(name: name, age: Int(age) ?? 18, country: country))}, label: { Text("Add person" )})
}
}
}
}
}
/// View to show peoples details and allow some of these details to be edited.
struct ShowAge: View {
#ObservedObject var person: Person
#State private var showAgeEditor = false
var body: some View {
VStack {
/// Show the current age.
Text("\(self.person.name)")
Text("\(self.person.model.personsAge)")
Text("\(self.person.model.country)")
Image(systemName: "keyboard")
.onTapGesture {
self.showAgeEditor = true
}
/// Present the sheet to update the age.
.sheet(isPresented: $showAgeEditor) {
SheetView(showAgeEditor: self.$showAgeEditor)
.environmentObject(self.person)
.frame(minWidth: 300, minHeight: 400)
}
}
}
}
/// Sheet to allow editing of persons details.
struct SheetView: View {
#EnvironmentObject var person: Person
#Binding var showAgeEditor: Bool
let maxAge = 21
let minAge = 16
var body: some View {
return VStack(alignment: .leading) {
Text("Name: \(person.name)")
Stepper(value: $person.model.personsAge, in: minAge...maxAge, step: 1) {
Text("Age: \(person.model.personsAge)")
}
Text("Country: \(person.model.country)")
}
}
}
/// ViewModel that creates the Person Object to stored.
class Person: ObservableObject, Identifiable {
#Published var model: LegalDrinkingAge
var name: String
var id = UUID()
init(name: String, age:Int, country: LegalAge) {
self.name = name
self.model = Person.createLegalAge(drinkingAge: country.rawValue, country: "\(country)", personsAge: age)
}
private static func createLegalAge(drinkingAge: Int, country: String, personsAge: Int) -> LegalDrinkingAge {
LegalDrinkingAge(drinkingAge: drinkingAge, country: country, personsAge: personsAge)
}
func updateAge(_ age: Int) {
model.personsAge = age
}
}
struct LegalDrinkingAge {
var drinkingAge: Int = 0
var country: String = ""
var personsAge: Int = 0
mutating func setDrinkingAge(age: Int, country: String, personsAge: Int) {
drinkingAge = age
self.country = country
self.personsAge = personsAge
}
}
Separate view and make it observed for person
class StorePersonRowView: View {
#ObservedObject person: Person
var body: some View {
HStack {
Text("\(person.name)")
Text("\(person.model.personsAge)")
Text("\(person.model.country)")
Image(systemName: (person.model.personsAge >= person.model.drinkingAge) ? "checkmark.shield.fill" : "xmark.shield.fill").foregroundColor((person.model.personsAge >= person.model.drinkingAge) ? Color.green : Color.red)
}
}
}
and use it in link
ForEach(personStore.store) { person in
NavigationLink(destination: ShowAge(person: person)) {
StorePersonRowView(person: person)
}
}

Use a Modal to Add an Item from a List in SwiftUI

I would like to know how to add an item from a List included in a Modal so when I tap in a row, I can have the item selected in the First View that launched the sheet (to make clearer, the effect the you find when the iPhone app "Messages" select a contact from "Contacts").
Here's my basic code
struct Product : Hashable {
var name : String
init(name: String) {
self.name = name
}
func hash(into hasher: inout Hasher) {
hasher.combine(name)
}
}
class Manager {
var product : [Product] = []
init() {
let pencil = Product(name: "Pencil")
let eraser = Product(name: "Eraser")
let ruler = Product(name: "Notebook")
product = [pencil, eraser, ruler]
}
}
struct FirstView: View {
#State var isSheetOpened = false
var products : Manager
var body: some View {
VStack {
Button(action: {
self.isSheetOpened.toggle()
}) {
Text("Add item from sheet")
}
.sheet(isPresented: self.$isSheetOpened) {
Sheet(products: self.products, isSheetOpened: self.isSheetOpened)
}
Text("Add here")
}
}
}
struct Sheet: View {
var products : Manager
var isSheetOpened : Bool
var body: some View {
VStack {
List {
ForEach(self.products.product, id: \.self) { index in
Text(index.name)
}
}
}
}
}
You would need to use #State in FirstView and #Binding to Sheet to show the selected item in FirstView.
Additionally, to dismiss the view after selecting the item in the Sheet, you can use the environment variable presentationMode.
Here's the code that does it. Hope it helps.
struct FirstView: View {
#State var isSheetOpened = false
#State var selectedProduct: String = ""
var products = Manager()
var body: some View {
VStack {
Button(action: {
self.isSheetOpened.toggle()
}) {
Text("Add item from sheet")
}
.sheet(isPresented: self.$isSheetOpened) {
Sheet(products: self.products, isSheetOpened: self.isSheetOpened, selectedProduct: self.$selectedProduct)
}
Text("\(selectedProduct)")
}
}
}
struct Sheet: View {
var products : Manager
var isSheetOpened : Bool
#Binding var selectedProduct: String
#Environment(\.presentationMode) var presentationMode
var body: some View {
VStack {
List {
ForEach(self.products.product, id: \.self) { index in
Button(action: {
self.selectedProduct = index.name
self.presentationMode.wrappedValue.dismiss()
}) {
Text(index.name)
}
}
}
}
}
}
check this out:
Because you are obviously missing some basic knowledge you should read about #Binding, ObservableObject, EnvironmentObject ...without it you will never be able to write an app in SwiftUI
import SwiftUI
struct Product : Hashable {
var name : String
init(name: String) {
self.name = name
}
func hash(into hasher: inout Hasher) {
hasher.combine(name)
}
}
class Manager : ObservableObject {
#Published var chosenProducts : [Product] = []
var products : [Product] = []
init() {
let pencil = Product(name: "Pencil")
let eraser = Product(name: "Eraser")
let ruler = Product(name: "Notebook")
products = [pencil, eraser, ruler]
}
}
struct ContentView: View {
#EnvironmentObject var manager : Manager
#State var isSheetOpened = false
var body: some View {
VStack {
Button(action: {
self.isSheetOpened.toggle()
}) {
Text("Add item from sheet")
}
Text("Chosen products")
.font(.largeTitle)
List {
ForEach(self.manager.chosenProducts, id: \.self) { product in
Text(product.name)
}
}
.sheet(isPresented: self.$isSheetOpened) {
Sheet(isSheetOpened: self.$isSheetOpened)
.environmentObject(self.manager)
}
Text("Add here")
}
}
}
struct Sheet: View {
#EnvironmentObject var manager : Manager
#Binding var isSheetOpened : Bool
var body: some View {
VStack {
List (self.manager.products, id: \.self) { product in
Button(action: {
self.manager.chosenProducts.append(product)
self.isSheetOpened = false
}) {
Text(product.name)
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().environmentObject(Manager())
}
}

Resources