SwiftUI会自动滚动到ScrollView的底部(从下至上)

时间:2019-10-14 12:20:37

标签: foreach scroll scrollview swiftui swiftui-list

我的问题是(在SwiftUI中)我有一个带有foreach的ScrollView。知道foreach何时加载所有我的条目,我希望最后一个条目集中。

我做了一些Google研究,但找不到任何答案。

  ScrollView {
    VStack {
      ForEach (0..<self.entries.count) { index in
        Group {
          Text(self.entries[index].getName())
        }
      }
    }
  }

3 个答案:

答案 0 :(得分:10)

滚动到底部进行更改

我还没有足够的声望来发表评论,所以你去@Dam 和@Evert

要在 ForEach 中的条目数量发生变化时滚动到底部,您还可以使用与 ScrollViewReader 相同的方法,如上述答案中所述,通过像这样添加视图修饰符 onChange

struct ContentView: View {
    let colors: [Color] = [.red, .blue, .green]
    var entries: [Entry] = Array(repeating: Entry(), count: 10)

    var body: some View {
        ScrollView {
            ScrollViewReader { value in
                            
                ForEach(0..<entries.count) { i in
                    Text(self.entries[i].getName())
                        .frame(width: 300, height: 200)
                        .background(colors[i % colors.count])
                        .padding(.all, 20)
                }
                .onChange(of: entries.count) { _ in
                    value.scrollTo(entries.count - 1)
                }
            }
        }
    }
}

答案 1 :(得分:8)

带有ScrollViewReader的iOS14解决方案

iOS14中的

SwiftUI获得了一项新功能。为了能够在ScrollView中滚动到特定位置。有一种名为ScrollViewReader的新类型,其作用类似于Geometry Reader

下面的代码将滚动到视图中的最后一项。我重用了您的代码,并添加了一些颜色以实现更好的可视化效果。所以我猜这是您的“ Entry”结构:

struct Entry {
    static var index = 0
    var name = "Entry number "
    
    func getName() -> String {
         Entry.index += 1
         return self.name + "\(Entry.index)"
    }
}

主要的ContentView:

struct ContentView: View {
    let colors: [Color] = [.red, .blue, .green]
    var entries: [Entry] = Array(repeating: Entry(), count: 10)

    var body: some View {
        ScrollView {
            ScrollViewReader { value in
                            
                ForEach(0..<entries.count) { i in
                    Text(self.entries[i].getName())
                        .frame(width: 300, height: 200)
                        .background(colors[i % colors.count])
                        .padding(.all, 20)
                }
                .onAppear {
                    value.scrollTo(entries.count - 1, anchor: .center)
                }
            }
        }
    }
}

尝试在WWDC20宣布的新版SwiftUI中运行此命令。我认为这是一个很大的增强。

答案 2 :(得分:1)

根据 Apple 的 documentation on ScrollViewReader,它应该包装滚动视图,而不是嵌套。

来自 Apple 文档的示例:

@Namespace var topID
@Namespace var bottomID

var body: some View {
    ScrollViewReader { proxy in
        ScrollView {
            Button("Scroll to Bottom") {
                withAnimation {
                    proxy.scrollTo(bottomID)
                }
            }
            .id(topID)

            VStack(spacing: 0) {
                ForEach(0..<100) { i in
                    color(fraction: Double(i) / 100)
                        .frame(height: 32)
                }
            }

            Button("Top") {
                withAnimation {
                    proxy.scrollTo(topID)
                }
            }
            .id(bottomID)
        }
    }
}

func color(fraction: Double) -> Color {
    Color(red: fraction, green: 1 - fraction, blue: 0.5)
}

This other article 还展示了如何使用 ScrollViewReader 来包装 List 元素,这也很方便。文章中的代码示例:

ScrollViewReader { scrollView in
    List {
        ForEach(photos.indices) { index in
            Image(photos[index])
                .resizable()
                .scaledToFill()
                .cornerRadius(25)
                .id(index)
        }
    }
 
    .
    .
    .
}

对我来说,使用 value.scrollTo(entries.count - 1) 不起作用。此外,使用 value.scrollTo(entries.last?.id) 也不起作用,直到我使用 .id(...) 视图修饰符(可能是因为我的条目不符合 Identifiable)。

使用 ForEach,这是我正在处理的代码:

ScrollViewReader { value in
    ScrollView {
        ForEach(state.messages, id: \.messageId) { message in
            MessageView(message: message)
                .id(message.messageId)
        }
    }
    .onAppear {
        value.scrollTo(state.messages.last?.messageId)
    }
    .onChange(of: state.messages.count) { _ in
        value.scrollTo(state.messages.last?.messageId)
    }
}

使用 onAppear 时的一个重要注意事项是在 ScrollView/List 上使用该修饰符,而不是在 ForEach 中使用。在 ForEach 中执行此操作将导致它在手动滚动时触发列表中的元素。