I am new to SwiftUI and trying to load a list from a json file which is locally saved. I am able display the json list but I want to scroll it to a specific index in the list. Following is my code in View:
struct SignUpEnterPhoneNumberView: View {
@StateObject private var presenter = CountryListPresenter()
@State private var searchText = ""
var body: some View {
VStack {
NavigationStack {
ScrollView {
ScrollViewReader { proxy in
ForEach(presenter.countries, id: \.self) { country in
HStack(spacing: 10) {
Text(country.emoji ?? "")
Text(country.phone)
Text(country.name)
Spacer()
}
}
.onChange(of: presenter.countries.count, perform: { _ in
proxy.scrollTo(30)
})
}
}
.navigationBarTitle(Text("Select Country Code"), displayMode: .inline)
Spacer()
}
.searchable(text: $searchText, prompt: presenter.searchTitle)
}
}
}
My presenter file:
class CountryListPresenter: ObservableObject {
let navigationTitle: String
let searchTitle: String
var countries = [CountryInfoValue]()
var selectedIndex = 50
init(
) {
navigationTitle = "Select Country Code"
searchTitle = "Search"
fetchCountryList()
}
func fetchCountryList() {
guard let url = Bundle.main.url(forResource: "countries.emoji", withExtension: "json") else {
return
}
let data = try? Data(contentsOf: url)
if let data = data {
let countryInfo = try? JSONDecoder().decode(CountryInfo.self, from: data)
var countryList = countryInfo?.map { $0.value }
countryList = countryList?.sorted(by: { $0.name < $1.name })
guard let countryList = countryList else {
return
}
countries = countryList
}
}
}
I have tried both onAppear and onChange but nothing works for me.
Please try to declare this as
@Published var countries = [CountryInfoValue]()
If you want to listen to changes of field of ObservableObject class you should declare them @Published so they can update listeners when its value changed.
I believe since its not published its empty state is copied to state. And you cant scrollTo item because its empty.
Code below works as intented. I tried to copy your logic there.
struct ContentView: View {
@StateObject var viewModel = SomeViewMode()
var body: some View {
ScrollViewReader { proxy in
ScrollView {
ForEach($viewModel.items.indices,id:\.self) { index in
ScrollItem()
.onAppear {
proxy.scrollTo(30)
}
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct ScrollItem: View {
var body: some View {
Rectangle()
.fill(Color.red)
.frame(minHeight: 300)
}
}
class SomeViewMode : ObservableObject {
@Published var items = [Int]()
init() {
DispatchQueue.main.asyncAfter(deadline: .now() + 5) { [weak self] in
self?.items = Array(repeating:Int.random(in: 0..<1000), count: 50)
}
}
}