ScrollViewReader 不以编程方式滚动
ScrollViewReader not scrolling programmatically
我有以下视图,当元素添加到列表时,我试图让它在单击按钮时滚动到底部。我已经搜索并发现 ScrollViewReader 是可以使用的选项,但是我的实现似乎不起作用。
我的修复尝试包括在内部视图和外部视图上显式设置单元格的 ID HStack{}
我什至尝试将 id 设置为自身的引用,有点知道那是一个坏主意,但为简洁起见。我还删除了列表中的任何额外视图,例如 HStack{}
、Spacer()
等。只是让我的 ColorsChosenView().id(i)
认为额外的视图可能会导致它,但我仍然离题了坚持。
var body: some View {
VStack {
ScrollViewReader { reader in
List {
ForEach(0..<vm.guesses.count, id: \.self) { i in
HStack{
Spacer()
ColorsChosenView(locationCorrect: 1,
locationIncorrect: 3,
color1: vm.guesses[i][0],
color2: vm.guesses[i][1],
color3: vm.guesses[i][2],
color4: vm.guesses[i][3])
Spacer()
}.id(i)
}
}.listStyle(InsetListStyle())
Divider()
.frame(maxWidth: 250)
ColorChoicePicker(vm: vm)
Divider()
.frame(maxWidth: 250)
HStack {
Spacer()
FABButton(text: "SUBMIT")
.onTapGesture {
vm.submit()
reader.scrollTo(vm.guesses.count - 1)
}
}.padding()
}
}
.navigationBarHidden(true)
.navigationBarHidden(true)
.onAppear(perform: {
vm.resetGame()
})
}
为了简化事情,我发现这工作得很好。然而我的实现并没有太大的不同。
var body: some View {
ScrollViewReader { proxy in
VStack {
Button("Jump to #50") {
proxy.scrollTo(50)
}
List(0..<100, id: \.self) { i in
Text("Example \(i)")
.id(i)
}
}
}
}
由于您正在修改数组,因此这应该有效:
1:在主线程中调用函数(DispatchQueue.main.async
)
-> 这将“有点”起作用,它会滚动但不会滚动到当前项目而是上一个项目
2:(解决方法)在更改处理程序中处理滚动(如果所有更改都应使其滚动到底部,您也可以删除 shouldScroll
变量)
class NumbersContainer: ObservableObject {
@Published var numbers: [Int] = Array(0..<25)
func submit() {
self.numbers.append(self.numbers.count)
}
}
struct ContentView: View {
@StateObject var nc = NumbersContainer()
@State var shouldScroll: Bool = false
var body: some View {
VStack {
ScrollViewReader { reader in
Button("Submit", action: {
DispatchQueue.main.async {
nc.submit()
}
self.shouldScroll = true
})
List {
ForEach(0..<nc.numbers.count, id: \.self) { i in
HStack {
Spacer()
Text("Row \(i)")
Spacer()
}.id(i)
}
}
.onChange(of: nc.numbers) { newValue in
if shouldScroll {
reader.scrollTo(newValue.count - 1)
shouldScroll = false
}
}
}
}
}
}
另一种可能性是使用 ScrollReaderProxy 作为提交函数的参数:
class NumbersContainer: ObservableObject {
@Published var numbers: [Int] = Array(0..<25)
func submit(reader: ScrollViewProxy) {
let dispatchGroup = DispatchGroup()
dispatchGroup.enter() // All leaves must have an enter
DispatchQueue.main.async {
self.numbers.append(self.numbers.count)
dispatchGroup.leave() // Notifies the DispatchGroup
}
dispatchGroup.notify(queue: .main) {
reader.scrollTo(self.numbers.count - 1)
}
}
}
struct ContentView: View {
@StateObject var nc = NumbersContainer()
var body: some View {
VStack {
ScrollViewReader { reader in
Button("Submit", action: {
nc.submit(reader: reader)
})
List {
ForEach(0..<nc.numbers.count, id: \.self) { i in
HStack {
Spacer()
Text("Row \(i)")
Spacer()
}.id(i)
}
}
}
}
}
}
我有以下视图,当元素添加到列表时,我试图让它在单击按钮时滚动到底部。我已经搜索并发现 ScrollViewReader 是可以使用的选项,但是我的实现似乎不起作用。
我的修复尝试包括在内部视图和外部视图上显式设置单元格的 ID HStack{}
我什至尝试将 id 设置为自身的引用,有点知道那是一个坏主意,但为简洁起见。我还删除了列表中的任何额外视图,例如 HStack{}
、Spacer()
等。只是让我的 ColorsChosenView().id(i)
认为额外的视图可能会导致它,但我仍然离题了坚持。
var body: some View {
VStack {
ScrollViewReader { reader in
List {
ForEach(0..<vm.guesses.count, id: \.self) { i in
HStack{
Spacer()
ColorsChosenView(locationCorrect: 1,
locationIncorrect: 3,
color1: vm.guesses[i][0],
color2: vm.guesses[i][1],
color3: vm.guesses[i][2],
color4: vm.guesses[i][3])
Spacer()
}.id(i)
}
}.listStyle(InsetListStyle())
Divider()
.frame(maxWidth: 250)
ColorChoicePicker(vm: vm)
Divider()
.frame(maxWidth: 250)
HStack {
Spacer()
FABButton(text: "SUBMIT")
.onTapGesture {
vm.submit()
reader.scrollTo(vm.guesses.count - 1)
}
}.padding()
}
}
.navigationBarHidden(true)
.navigationBarHidden(true)
.onAppear(perform: {
vm.resetGame()
})
}
为了简化事情,我发现这工作得很好。然而我的实现并没有太大的不同。
var body: some View {
ScrollViewReader { proxy in
VStack {
Button("Jump to #50") {
proxy.scrollTo(50)
}
List(0..<100, id: \.self) { i in
Text("Example \(i)")
.id(i)
}
}
}
}
由于您正在修改数组,因此这应该有效:
1:在主线程中调用函数(DispatchQueue.main.async
)
-> 这将“有点”起作用,它会滚动但不会滚动到当前项目而是上一个项目
2:(解决方法)在更改处理程序中处理滚动(如果所有更改都应使其滚动到底部,您也可以删除 shouldScroll
变量)
class NumbersContainer: ObservableObject {
@Published var numbers: [Int] = Array(0..<25)
func submit() {
self.numbers.append(self.numbers.count)
}
}
struct ContentView: View {
@StateObject var nc = NumbersContainer()
@State var shouldScroll: Bool = false
var body: some View {
VStack {
ScrollViewReader { reader in
Button("Submit", action: {
DispatchQueue.main.async {
nc.submit()
}
self.shouldScroll = true
})
List {
ForEach(0..<nc.numbers.count, id: \.self) { i in
HStack {
Spacer()
Text("Row \(i)")
Spacer()
}.id(i)
}
}
.onChange(of: nc.numbers) { newValue in
if shouldScroll {
reader.scrollTo(newValue.count - 1)
shouldScroll = false
}
}
}
}
}
}
另一种可能性是使用 ScrollReaderProxy 作为提交函数的参数:
class NumbersContainer: ObservableObject {
@Published var numbers: [Int] = Array(0..<25)
func submit(reader: ScrollViewProxy) {
let dispatchGroup = DispatchGroup()
dispatchGroup.enter() // All leaves must have an enter
DispatchQueue.main.async {
self.numbers.append(self.numbers.count)
dispatchGroup.leave() // Notifies the DispatchGroup
}
dispatchGroup.notify(queue: .main) {
reader.scrollTo(self.numbers.count - 1)
}
}
}
struct ContentView: View {
@StateObject var nc = NumbersContainer()
var body: some View {
VStack {
ScrollViewReader { reader in
Button("Submit", action: {
nc.submit(reader: reader)
})
List {
ForEach(0..<nc.numbers.count, id: \.self) { i in
HStack {
Spacer()
Text("Row \(i)")
Spacer()
}.id(i)
}
}
}
}
}
}