|
In you ObervableObject add this variable
static var sample = [Family(name: "Family One", peoples: [People(name: "Person One", age: 21), People(name: "Person Two", age: 22)])]
then you can get family by:
Families.sample[0]
to get people array in family
Families.sample[0].peoples
or in preview you can create them directly. This is for preview so you need to pass some sample data.
|
|
this is my struct an class file
import Foundation
struct Family: Codable, Identifiable, Equatable{
var id = UUID()
let name: String
var peoples: [People]
static func == (lhs: Family, rhs: Family) -> Bool {
return lhs.id == rhs.id
}
}
struct People: Codable, Identifiable {
var id = UUID()
let name: String
let age: Int
}
class Families: ObservableObject {
static var sample = [Family(name: "Family One", peoples: [People(name: "Person One", age: 21), People(name: "Person Two", age: 22)])]
@ Published var families = [Family]() {
didSet {
if let encoded = try? JSONEncoder().encode(families) {
UserDefaults.standard.set(encoded, forKey: "Items")
}
}
}
init() {
if let savedItems = UserDefaults.standard.data(forKey: "Items") {
if let decodedItems = try? JSONDecoder().decode([Family].self, from: savedItems) {
families = decodedItems
return
}
}
families = []
}
}
and adont know what to do
import SwiftUI
struct NovyClovek: View {
@ObservedObject var people: Families
@Environment(\.dismiss) var dismiss
var family: Family
@State private var name = ""
@State private var age = 11
var body: some View {
NavigationView{
List{
TextField("meno", text: $name)
TextField("vek", value: $age, formatter: NumberFormatter())
}
.navigationTitle("nove")
.toolbar {
Button ("save") {
let newPerson = People(name: name, age: age)
if let index = people.families.firstIndex(where: { $0.id == family.id }) {
people.families[index].peoples.append(newPerson)
dismiss()
}
}
}
}
}
}
struct NovyClovek_Previews: PreviewProvider {
static var previews: some View {
NovyClovek(people: Families(), family: Families.sample[0].peoples)
}
}
|
|
Ok, then better remove static var and place that directly into your preview
NovyClovek(people: Families(), family: Family(name: "Family One", peoples: [People(name: "Person One", age: 11)]))
|
|
now I have no errors. unfortunately, they still do not place members in individual families
|
|
Share your ContentView file.
|
|
import SwiftUI
struct ContentView: View {
@StateObject var family = Families()
@State private var showingAddExpense = false
var body: some View {
NavigationView{
List{
ForEach(family.families) { familia in
//Text(familia.name)
NavigationLink{
ListFamily()
}label: {
Text(familia.name)
}
}
.onDelete(perform: removeItems)
}
.navigationTitle("Rodina")
.toolbar {
Button {
showingAddExpense = true
} label: {
Image(systemName: "plus")
}
}
.sheet(isPresented: $showingAddExpense) {
NewFamily(family: family)
}
}
}
func removeItems(at offsets: IndexSet) {
family.families.remove(atOffsets: offsets)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
|
|
import SwiftUI
struct ListFamily: View {
@StateObject var family = Families()
@State private var showingAddExpense = false
var body: some View {
NavigationView{
List{
ForEach(family.families) { familia in
ForEach(familia.peoples) {person in
Text(person.name)
}
}
.onDelete(perform: removeItems)
}
.navigationTitle("dsdfs")
.toolbar {
Button {
showingAddExpense = true
} label: {
Image(systemName: "plus")
}
}
.sheet(isPresented: $showingAddExpense) {
NewPeople(people: Families(), family: Family(name: "Family One", peoples: [People(name: "Person One", age: 11)]))
}
}
}
func removeItems(at offsets: IndexSet) {
family.families.remove(atOffsets: offsets)
}
}
struct ListFamily_Previews: PreviewProvider {
static var previews: some View {
ListFamily()
}
}
|
|
In your ContentView you create an object @StateObject var family = Families() . Then you loop through it and follow a navlink that leads you to ListFamily . But in there you create absolutely new object @StateObject var family = Families() . In order to pass the object you either have to inject it as @ObservedObject var family: [Family] or inject that object via Environment. Otherwise you are working with two different objects which are not conneted to each other.
|
|
Can you please show me how to fix it? I'm at my wits end.
|
|
Can you upload that to github and share the link. Looking at those files you posted I am already confused what are you planning to do. As i understood you want to create a family then add people in there? I posted sample before, can you just copy paste that code in your Xcode, run it and say if this is the behavior you would like to have. This is what I posted in your previous thread.
https://www.hackingwithswift.com/forums/swiftui/how-to-create-items-in-items/20488/20534. Just add your data model as well to make it compile.
|
|
|
|
To make the code compile from github you just need to update few lines for in previews:
-
FamilyMembersView.swift
struct FamilyMembersView_Previews: PreviewProvider {
static var previews: some View {
FamilyMembersView(people: .constant([People]()), familyName: "Test")
}
}
-
AddPersonView.swift
struct AddPersonView_Previews: PreviewProvider {
static var previews: some View {
AddPersonView(people: .constant([People]()))
}
}
and that's it.
|
|
Thanks a lot.You are my hero
|
|
As I already completed another approach post it as well. So you can choose. This one maybe more easier to follow that the one with lots of binding I used before.
-
So let's start with ContentView.swift
struct ContentView: View {
@StateObject var family = Families()
@State private var showAddFamily = false
var body: some View {
NavigationStack {
List{
ForEach(family.families) { familia in
NavigationLink{
ListFamily(family: family, familia: familia)
} label: {
Text(familia.name)
}
}
.onDelete(perform: removeItems)
}
.navigationTitle("Rodina")
.toolbar {
Button {
showAddFamily = true
} label: {
Image(systemName: "plus")
}
}
.sheet(isPresented: $showAddFamily) {
NewFamily(family: family)
}
}
}
func removeItems(at offsets: IndexSet) {
family.families.remove(atOffsets: offsets)
}
}
|
|
- Then let's add
FamilyView .
struct ListFamily: View {
@ObservedObject var family: Families
var familia: Family
@State private var showingAddExpense = false
/* We need id to "cheat" a bit our view
We have an issue with @Published variable as
we first add family in [Family] array
So @Published publish changes and our view refreshes
But when we add a person to family.peoples we do not
change @Published property as nothing added to [Family]
as we change peoples inside it, so the variable is not changed
per se and it does't notify us.
So we use that id to change every time when we press + button
and make the view refresh as UUID will be
different every time. Better to find the way to make @Published
property change when underlying data in peoples array added.
*/
@State private var id = UUID()
var body: some View {
List {
ForEach(familia.peoples) { person in
Text(person.name)
}
.onDelete(perform: removeItems)
}
.toolbar {
Button {
showingAddExpense = true
} label: {
Image(systemName: "plus")
}
}
.sheet(isPresented: $showingAddExpense) {
NewPeople(family: family, familia: familia)
}
.onChange(of: showingAddExpense) { _ in
id = UUID()
}
}
func removeItems(at offsets: IndexSet) {
if let index = family.families.firstIndex(where: { familia.id == $0.id }) {
family.families[index].peoples.remove(atOffsets: offsets)
}
}
}
struct ListFamily_Previews: PreviewProvider {
static var previews: some View {
NavigationStack {
ListFamily(family: Families(), familia: Families.sample[0])
}
}
}
|
|
- And with
NewFamily we will add new families to our object
struct NewFamily: View {
@Environment(\.dismiss) var dismiss
@ObservedObject var family: Families
@State private var name = ""
var body: some View {
NavigationStack {
VStack {
TextField("New family", text: $name)
.textFieldStyle(.roundedBorder)
Button("Save") {
let newFamily = Family(name: name, peoples: [])
// We got name for new family so let's add it to our family object
family.families.append(newFamily)
dismiss()
}
.buttonStyle(.borderedProminent)
Spacer()
}
.padding()
.toolbar {
Button("Close") {
dismiss()
}
}
}
}
}
struct NewFamily_Previews: PreviewProvider {
static var previews: some View {
NavigationStack {
NewFamily(family: Families())
}
}
}
- With
NewPeople we will add new people to family
struct NewPeople: View {
@Environment(\.dismiss) var dismiss
@ObservedObject var family: Families
var familia: Family
@State private var name = ""
@State private var age = 10
var body: some View {
NavigationStack {
VStack {
TextField("Name", text: $name)
.textFieldStyle(.roundedBorder)
TextField("Age", value: $age, format: .number)
.textFieldStyle(.roundedBorder)
Button("Save") {
let newPerson = People(name: name, age: age)
// We got new person, so lets find where it is in our family object in families array
// And add that person accordingly
if let index = family.families.firstIndex(where: { familia.id == $0.id }) {
family.families[index].peoples.append(newPerson)
}
dismiss()
}
.buttonStyle(.borderedProminent)
Spacer()
}
.padding()
.toolbar {
Button("Close") {
dismiss()
}
}
}
}
}
struct NewPeople_Previews: PreviewProvider {
static var previews: some View {
NavigationStack {
NewPeople(family: Families(), familia: Families.sample[0])
}
}
}
- and
DataModel is the same basically
struct Family: Codable, Identifiable, Equatable{
var id = UUID()
let name: String
var peoples: [People]
static func == (lhs: Family, rhs: Family) -> Bool {
return lhs.id == rhs.id
}
}
struct People: Codable, Identifiable {
var id = UUID()
let name: String
let age: Int
}
class Families: ObservableObject {
static var sample = [Family(name: "Family One", peoples: [People(name: "Person One", age: 21), People(name: "Person Two", age: 22)])]
@Published var families = [Family]() {
didSet {
if let encoded = try? JSONEncoder().encode(families) {
UserDefaults.standard.set(encoded, forKey: "Items")
}
}
}
init() {
if let savedItems = UserDefaults.standard.data(forKey: "Items") {
if let decodedItems = try? JSONDecoder().decode([Family].self, from: savedItems) {
families = decodedItems
return
}
}
families = []
}
}
|
|
|
|
I have some isue. How to add other items under people
import SwiftUI
struct NewNotes: View {
@Environment(\.dismiss) var dismiss
@ObservedObject var family: Families
@State private var newNote = ""
@State private var zaznamCislo = 1
var familia: Vcelnica
var ule: Ule
var body: some View {
NavigationStack{
Form {
VStack{
TextField("Note", text: $newNote)
.textFieldStyle(.roundedBorder)
Button("save") {
// let zaznam = Zanam(zaznmaCislo: zaznamCislo, zaznamObsah: newNote)
// if let index = family.families.firstIndex(where: { familia.id == $0.id }) {
// if let index2 = family.families[index].ul.firstIndex(where: {ule.id == $0.id}) {
// family.families[index].ul[index2].zaznamy.append(zaznam)
// }
// }
// dismiss()
let index1 = family.families.firstIndex(where: { familia.id == $0.id })
let zaznam = Zanam(zaznmaCislo: zaznamCislo, zaznamObsah: newNote)
if let index = ule.zaznamy.firstIndex(where: {ule.id == $0.id}) {
family.families[index1 ?? 0].ul[index].zaznamy.append(zaznam)
}
}
.buttonStyle(.bordered)
}
}
//.padding()
.toolbar {
Button("Close") {
dismiss()
}
}
}
}
}
struct NewNotes_Previews: PreviewProvider {
static var previews: some View {
NewNotes(family: Families(), familia: Families.sample[0], ule: Ule(name: "", cisloUla: 21, cisloMatky: 1, farbaMatky: "", age: 1, zaznamy: []))
}
}
|
|
struct Vcelnica: Codable, Identifiable, Equatable{
var id = UUID()
let name: String
var ul: [Ule]
static func == (lhs: Vcelnica, rhs: Vcelnica) -> Bool {
return lhs.id == rhs.id
}
}
struct Ule: Codable, Identifiable{
var id = UUID()
let name: String
let cisloUla: Int
let cisloMatky: Int
let farbaMatky: String
let age: Int
var zaznamy: [Zanam]
}
struct Zanam: Codable, Identifiable {
var id = UUID()
let zaznmaCislo: Int
let zaznamObsah: String
}
class Families: ObservableObject {
static var sample = [Vcelnica(name: "Family One", ul: [Ule(name: "Person One", cisloUla: 1, cisloMatky: 1, farbaMatky: "modra", age: 1, zaznamy: [(Zanam(zaznmaCislo: 1, zaznamObsah: "test1"))] ), Ule(name: "Person Two", cisloUla: 2, cisloMatky: 2, farbaMatky: "modra", age: 2, zaznamy: [(Zanam(zaznmaCislo: 2, zaznamObsah: "test2"))])])]
@Published var families = [Vcelnica]() {
didSet {
if let encoded = try? JSONEncoder().encode(families) {
UserDefaults.standard.set(encoded, forKey: "Items")
}
}
}
init() {
if let savedItems = UserDefaults.standard.data(forKey: "Items") {
if let decodedItems = try? JSONDecoder().decode([Vcelnica].self, from: savedItems) {
families = decodedItems
return
}
}
families = []
}
}
|
|
As you already have Family conform to Equatable you can use firstIndex(of:) method. The logic should follow as:
// First find the index of Family to make modifications
if let familyIndex = family.families.firstIndex(of: familia) {
// Second find the index of Ul to make modifications
if let ulIndex = family.families[familyIndex].ul.firstIndex(where: {ule.id == $0.id}) {
let zaznam = Zanam(zaznmaCislo: zaznamCislo, zaznamObsah: newNote)
// Then add your zaznam
family.families[familyIndex].ul[ulIndex].zaznamy.append(zaznam)
}
}
well if no typos from my side, this should work
|
|
something went wrong
Dont work :)
|
|
Have you tried to run on simulator? I suspect, the item might be added but it does not appear after addition. The issue is the same, you change the underlying value of @Published property, and as there is no change in property itself it does not publish the change. Or the item is not added at all?
|
|
it seems that the item will not be added. I think I've tried all the combinations, but it's not enough with my knowledge
|
|
|
|
|
|
I'd like to, but with current state of the project I have no idea what are you trying to do there. I tried to clone your project but on Preview it says Active Scheme does not build this file. No idea what is wrong with preview even.
|
|
So exactly...
the project will be for an apiary
In the first preview (family) I will create apiaries (I have more)
In the second view (people) I will create hives in the given apiary
in the third preview, I want to make a report (notes) for each beehive.
|
|
As there are too many changes you want to implement, I have revised the logic. You will want to adjust to your data which you'd like to collect. But this one is more or less workable. Though it is still not good idea to save everything to UserDefaults. It's recommended to keep it less than 100kb. Otherwise it will affect app startup.
https://github.com/ygeras/Beehives.git
|
|
Which storage method is better?
|
|
|
|
Hey ,Please help me anyone here
|
|
Here's an updated version of your code with the necessary modifications:
swift
Copy code
import SwiftUI
struct NovyClovek: View {
@ObservedObject var people: Families
@Environment(.dismiss) var dismiss
var family: Family
@State private var name = ""
@State private var age = 11
var body: some View {
NavigationView {
List {
TextField("Name", text: $name)
TextField("Age", value: $age, formatter: NumberFormatter())
}
.navigationTitle("New Person")
.toolbar {
Button("Save") {
let newPerson = People(name: name, age: age)
if let index = people.families.firstIndex(of: family) {
people.families[index].peoples.append(newPerson)
dismiss()
}
}
}
}
}
}
struct NovyClovek_Previews: PreviewProvider {
static var previews: some View {
NovyClovek(people: Families(), family: Family())
}
}
Make sure that the Family and People structs and the Families class are defined correctly. Additionally, ensure that you pass the appropriate Family object when navigating to the NovyClovek view.
With these modifications, you should be able to add a new person to the selected family.
|
|
Got it alot. Perfactly. Thanks
|