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