Reputation: 284
My code is a little more complex than this so I created an example that gets the same error.
When I navigate into a view, I have a function I want to perform with a variable passed into this view. That function then produces an array. I then want to put that array into a List, but I get an error.
How do I get the List to show the produced array?
I think the issue is the List can't be updated because it already has the declared blank array.
struct ContentView : View {
@State var array = [String]()
var body: some View {
List(self.array,id: \.self) { item in
Text("\(item)")
}
.onAppear(perform: createArrayItems)
}
func createArrayItems() {
array = ["item1", "item2", "item3", "item4", "item5"]
}
}
Upvotes: 16
Views: 26864
Reputation: 543
Use this:
class ObservableArray<T>: ObservableObject {
@Published var array: [T]
init(array: [T] = ) {
self.array = array
}
init(repeating value: T, count: Int) {
array = Array(repeating: value, count: count)
}
}
struct YourView: View {
@ObservedObject var array = ObservableArray<String>()
var body: some View {
}
}
Upvotes: 0
Reputation: 181
You can use ObservableObject data providers(eg : ViewModel) with @Published properties.
struct ListView: View {
@ObservedObject var viewModel = ListViewModel()
var body: some View {
NavigationView {
List(){
ForEach(viewModel.items) { item in
Text(item)
}
}
}
}
}
#if DEBUG
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ListView()
}
}
#endif
class ListViewModel: ObservableObject {
@Published var items = ["item1", "item2", "item3", "item4", "item5","item6"]
func addItem(){
items.append("item7")
}
}
Upvotes: 17
Reputation: 407
@txagPman
I too have your problem to understand how to modify a list. I was able to write this code. I hope it's useful.
import SwiftUI
struct ContentView: View {
@State private var array = createArrayItems()
// @State private var array = [""] - This work
// @State private var array = [] - This not work
@State private var text = ""
var body: some View {
VStack {
TextField("Text", text: $text, onCommit: {
// self.array = createArrayItems() - This work after press return on textfield
self.array.append(self.text)
}).padding()
List (self.array, id: \.self) {item in
Text("\(item)")
}
}
// .onAppear {
// self.array = createArrayItems() - This not work
// }
}
}
func createArrayItems() -> [String] {
return ["item_01","item_02","item_03","item_04" ]
}
Upvotes: 1
Reputation: 1659
You can use combine framework to update the list. Whenever a change is made in DataProvider Object it will automatically update the list.
struct ContentView : View {
@EnvironmentObject var data: DataProvider
var body: some View {
NavigationView {
NavigationButton(destination: SecondPage()) {
Text("Go to Second Page")
}
List {
ForEach(data.array.identified(by: \.self)) { item in
Text("\(item)")
}
}
}
}
}
Add items in the list
struct SecondPage : View {
@State var counter = 1
@EnvironmentObject var tempArray: DataProvider
var body: some View {
VStack {
Button(action: {
self.tempArray.array.append("item\(self.counter)")
self.counter += 1
}) {
Text("Add items")
}
Text("Number of items added \(counter-1)")
}
}
}
It will simply notify the change
import Combine
final class DataProvider: BindableObject {
let didChange = PassthroughSubject<DataProvider, Never>()
var array = [String]() {
didSet {
didChange.send(self)
}
}
}
You also need to do some update in the SceneDelegate. This update ensures that ContentView has a DataProvider object in the environment.
window.rootViewController = UIHostingController(rootView: ContentView().environmentObject(DataProvider()))
Upvotes: 1
Reputation: 2543
A dumb UI is a good UI
Keep your views dumb try the following code to create a dynamic List
import UIKit
import SwiftUI
import PlaygroundSupport
struct ContentView : View {
@State var array = [String]()
var body: some View {
List{
ForEach(array.identified(by: \.self)) { item in
Text("\(item)")
}
}
}
}
func createArrayItems()->[String] {
return ["item1", "item2", "item3", "item4", "item5","item6"]
}
PlaygroundPage.current.liveView = UIHostingController(rootView: ContentView(array: createArrayItems()))
Upvotes: 0