I am trying to use .id(_:)
to do some programmatic scrolling with a ForEach, such that a Form will scroll each time a new section is added to it using the ForEach. However, I am getting some undesirable behaviour and I would like to understand the mechanics of what is causing said behaviour. As you can see in the screenshot, the third text view isn't even showing up. When I remove the id
method, all three Text views show up as you'd expect.
import SwiftUI
struct ContentView: View {
@State private var array: [Int] = [1]
var body: some View {
ScrollViewReader { p in
NavigationView {
Form {
ForEach(self.array, id: \.self) { num in
Section {
Text("Hello")
Text("My name is slim shady")
Text("number \(num)")
}
.id(num)
.onChange(of: array.count) { count in
p.scrollTo((count 1), anchor: .top)
}
}
}
.toolbar {
ToolbarItem(placement: .bottomBar) {
Button("add") {
array.append(array.count 1)
}
}
}
}
}
}
}
Any help would be greatly appreciated! Cheers.
CodePudding user response:
You have a couple of issues going on here. I have never tried to put a Section
inside a ForEach
like that, but they obviously do not play well. I removed it and used a VStack
instead. I am not sure if you can make new sections like that, but that is another question for another day.
The issue you were having with your ScrollTo
is a misunderstanding of what id
you were actually using. When you instantiate a ForEach
with an array that is not otherwise identifiable, you use .self
as the id
. However, that means you are keeping track of the elements themselves, not the contents of each element. So, when you tried to put an int
into your scrollTo()
, nothing happened because an int
is not an element of your array. What you need to do is supply an element like this:
struct ContentView: View {
@State private var array: [Int] = [1]
var body: some View {
ScrollViewReader { p in
NavigationView {
Form {
ForEach(self.array, id: \.self) { num in
VStack(alignment: .leading) {
Text("Hello")
Text("My name is slim shady")
Text("number \(num)")
}
// num is an element of your array, not an int
.id(num)
.onChange(of: array.count) { count in
// Supply scrollTo with an array element here
p.scrollTo(array.last, anchor: .top)
}
}
}
.toolbar {
ToolbarItem(placement: .bottomBar) {
Button("add") {
array.append(array.count 1)
}
}
}
}
}
}
}
CodePudding user response:
Just in case anyone happens to read this, after a bunch of experimenting I have been able to make this work. It appears as though the issue that I posted in my screenshots was being caused by creating sections inside of the ForEach
, as Yrb mentioned. However, I tried a different initializer for creating sections and it seems to work as long as you provide a view for the section header. I have no idea why however, if anyone has any insight please share.
struct ContentView: View {
@State private var array: [Int] = [1]
var body: some View {
ScrollViewReader { p in
NavigationView {
Form {
ForEach(self.array, id: \Int.self) { num in
Section {
TextVeiwshg(num: num)
} header: {
Text("section #\(num)")
}
.id(num)
}
}
.toolbar {
ToolbarItem(placement: .bottomBar) {
Button("add") {
array.append(array.count 1)
print(array)
}
}
}
}
.onChange(of: array.count) { count in
print(array.count)
withAnimation {
p.scrollTo((count), anchor: .top)
}
print("scrolling to \(count)")
}
}
}
}