txagPman
txagPman

Reputation: 284

How do I update a List in SwiftUI?

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

Answers (5)

ImpactZero
ImpactZero

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

Nowfal E Salam
Nowfal E Salam

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

Cesare Piersigilli
Cesare Piersigilli

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

Md Shafiul Islam
Md Shafiul Islam

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

Imran
Imran

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

Related Questions