Reputation: 123
In my app I have an array of friends that can be added to from another view. I've stored this array of friends in a class which is an ObservableObject
. Within my main view, FriendListView
, I am using a ForEach
to display a custom view for each friend that a user has added.
I have not been able to add to the array and have the list display the new item. I have tried two different attempts based upon what I was able to understand and put together. Any help that someone is able to provide would be greatly appreciated. I currently have the method to display the 2nd view where I will be adding a new friend commented out and a method to automatically save a new one exists as a navigation bar item.
I was successful in displaying the list of friends if they were appended to the array during the class initializer. Calling the class method to add to the array from another view seems to work based upon me being able to print the contents of the array but the UI does not update.
This was completed by using a ForEach(0..<Array.count)
approach but I received the following console error after adding a new item.
Error
ForEach<Range<Int>, Int, HStack<TupleView<(Spacer, FriendView, Spacer)>>> count (4) != its initial count (3).
`ForEach(_:content:)` should only be used for *constant* data.
Instead conform data to `Identifiable` or use `ForEach(_:id:content:)` and provide an explicit `id`!
Model Code
struct Pet: Identifiable {
var id = UUID().uuidString
var name: String
var breed: String
}
class FriendModel: ObservableObject {
@Published var petList = [Pet]()
func addFriend(name: String, breed: String) {
petList.append(Pet(name: name, breed: breed))
}
init() {
petList.append(Pet(name: "Woofer", breed: "Dawg"))
petList.append(Pet(name: "Floofer", breed: "Dawg"))
petList.append(Pet(name: "Super", breed: "Dawg"))
// petList.append(Pet(name: "Dooper", breed: "Dawg"))
// petList.append(Pet(name: "Booper", breed: "Dawg"))
}
}
UI Code
struct FriendListView: View {
@EnvironmentObject var dataModel: FriendModel
@State private var displayAddFriendSheet = false
var body: some View {
GeometryReader { geo in
NavigationView {
ScrollView {
ForEach(0..<self.dataModel.petList.count) { pet in
HStack {
Spacer()
FriendView(
name: self.dataModel.petList[pet].name,
breed: self.dataModel.petList[pet].breed,
screenWidth: geo.size.width,
randomPic: Int.random(in: 1...2)
)
// FriendView(
// name: self.dataModel.petList[pet].name,
// breed: self.dataModel.petList[pet].breed,
// screenWidth: geo.size.width)
Spacer()
}
}
}// End of ScrollView
.navigationBarTitle(Text("Furiends"))
.navigationBarItems(trailing: Button(action: self.showAddFriend) {
Image(systemName: "plus")
}
)
.sheet(isPresented: self.$displayAddFriendSheet) {
AddFriendView()
}
}// End of NavigationView
}// End of GeometryReader geo1
}// End of body
func showAddFriend() {
// self.displayAddFriendSheet.toggle()
self.dataModel.addFriend(name: "Jax", breed: "Doodle")
print(dataModel.petList)
}// Enf of showAddFriend
}// End of FriendListView
After seeing the error from the first attempt, I read how I could skip the range and provide just the array as a part of the ForEach
since I am adhering to the Identifiable
protocol with my UUID in the Pet
struct. With the model code remaining the same as my first attempt, my updated UI code is below. Note the only change was to the ForEach but I received the following error:
Error
Cannot convert value of type '[Pet]' to expected argument type 'Range<Int>'
UI Code
struct FriendListView: View {
@EnvironmentObject var dataModel: FriendModel
@State private var displayAddFriendSheet = false
var body: some View {
GeometryReader { geo in
NavigationView {
ScrollView {
ForEach(self.dataModel.petList) { pet in // <--- Changed to directly reference the array
HStack {
Spacer()
FriendView(
name: self.dataModel.petList[pet].name,
breed: self.dataModel.petList[pet].breed,
screenWidth: geo.size.width,
randomPic: Int.random(in: 1...2)
)
// FriendView(
// name: self.dataModel.petList[pet].name,
// breed: self.dataModel.petList[pet].breed,
// screenWidth: geo.size.width)
Spacer()
}
}
}// End of ScrollView
.navigationBarTitle(Text("Furiends"))
.navigationBarItems(trailing: Button(action: self.showAddFriend) {
Image(systemName: "plus")
}
)
.sheet(isPresented: self.$displayAddFriendSheet) {
AddFriendView()
}
}// End of NavigationView
}// End of GeometryReader geo1
}// End of body
func showAddFriend() {
// self.displayAddFriendSheet.toggle()
self.dataModel.addFriend(name: "Jax", breed: "Doodle")
print(dataModel.petList)
}// Enf of showAddFriend
}// End of FriendListView
Upvotes: 7
Views: 1989
Reputation: 257711
I assume the remaining try to access by subscription confuses compiler, so as far as Pet
is Identifiable
the following should work
ForEach(self.dataModel.petList) { pet in
HStack {
Spacer()
FriendView(
name: pet.name, // << pet is not index
breed: pet.breed, // << so access by property
screenWidth: geo.size.width,
randomPic: Int.random(in: 1...2)
)
// .. other code
Upvotes: 1