【问题标题】:ScrollViewReader not scrolling programmaticallyScrollViewReader 不以编程方式滚动
【发布时间】:2021-09-19 14:59:40
【问题描述】:

我有以下视图,当元素添加到列表中时,我试图让它滚动到按钮单击的底部。我搜索并发现 ScrollViewReader 是可以使用的选项,但是我的实现似乎不起作用。

我的修复尝试包括在内部视图和外部 HStack{} 上显式设置单元格的 id 我什至尝试将 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)
                    }
                }
            }
        }

【问题讨论】:

  • 将损坏的代码作为“工作示例”真的很有用,其他代码工作的原因是因为您通过它拥有的索引识别一行,而第一个由对象本身标识
  • 我想这样做,但我不知道是什么原因造成的。我试图“哑巴”,但每次我这样做,它都奏效了。哈哈
  • 好的。现在我难住了。您现在拥有的确切实现对我有用。问题可能出在您的 vm-object 中。 submit() 函数是否会更改 guesses 变量?因为我尝试制作一个 ObservableObject 并且每当我更改变量然后滚动它都不起作用
  • 提交函数只是将猜测添加到数组中,然后填充到列表中。
  • 啊哈。罪魁祸首,您正在后台修改数组并在前台滚动。我已经编辑了我的答案并提供了 2 个解决方案

标签: list forms swiftui scrollview


【解决方案1】:

由于您正在修改数组,这应该可以工作:

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)
                    }
                }
            }
        }
    }
}

【讨论】:

  • 我将 ID 设置为 count 的枚举值的 ID。它应该是基于我的代码的索引。如果我将 ID 设置为 T&lt;Guess&gt; 则可以,但它设置为 T&lt;Int&gt; aka index of。
  • 哦,nvm 那我就是个白痴... :D 我想我找到了问题,我会改变答案
  • 我认为绑定会保持正确更新。太棒了,现在效果很好。
  • 是的,我曾经遇到过类似的问题,差点让我发疯。 :D
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-06
  • 1970-01-01
  • 2011-01-16
  • 2011-01-15
  • 2010-11-13
  • 2014-05-22
相关资源
最近更新 更多