当前位置:   article > 正文

SwiftUI之判断ScrollView是否滑动到底部及ChildSizeReader的使用_swiftui scrollview 判断滚动到底部

swiftui scrollview 判断滚动到底部

最近开发的项目中遇到一个需求,要在ScrollView滑动到底部的时候显示一个loading,然后请求第二页数据。这就涉及到怎么判断ScrollView滑动到底部了。

那么可以用这面这个条件判断

偏移量 >= (ScrollView内容高度 - ScrollView自身高度)

在用这个判断条件之前,得先获得到ScrollView内容高度和ScrollView自身高度,先介绍个小组件:ChildSizeReader

  1. struct ChildSizeReader<Content: View>: View {
  2. @Binding var size: CGSize
  3. let content: () -> Content
  4. var body: some View {
  5. ZStack {
  6. content()
  7. .background(
  8. GeometryReader { proxy in
  9. Color.clear
  10. .preference(key: SizePreferenceKey.self, value: proxy.size)
  11. }
  12. )
  13. }
  14. .onPreferenceChange(SizePreferenceKey.self) { preferences in
  15. self.size = preferences
  16. }
  17. }
  18. }
  19. private struct SizePreferenceKey: PreferenceKey {
  20. typealias Value = CGSize
  21. static var defaultValue: Value = .zero
  22. static func reduce(value _: inout Value, nextValue: () -> Value) {
  23. _ = nextValue()
  24. }
  25. }

ChildSizeReader组件接受两个值,sizecontent,这里的size用了@Binding修饰,需要调用的地方传个引用过来。content就不用多说了。举个例子用一下:

  1. struct ContentView: View {
  2. @State var scrollViewSize: CGSize = .zero
  3. @State var scrollViewContentSize: CGSize = .zero
  4. var body: some View {
  5. VStack {
  6. ChildSizeReader(size: $scrollViewSize) {
  7. ScrollView(.vertical, showsIndicators: false) {
  8. ChildSizeReader(size: $scrollViewContentSize) {
  9. VStack {
  10. ForEach(0..<30, id: \.self) { index in
  11. VStack {
  12. Text("This is the \(index)th line.")
  13. .padding()
  14. .frame(height: 30)
  15. .frame(maxWidth: .infinity, alignment: .leading)
  16. Color.gray
  17. .frame(height: 1)
  18. }
  19. }
  20. }
  21. .onAppear {
  22. print("scrollViewSize: \(scrollViewSize)")
  23. print("scrollViewContentSize: \(scrollViewContentSize)")
  24. }
  25. }
  26. }
  27. }
  28. }
  29. }
  30. }

上面代码中首先定义了两个属性,scrollViewSizescrollViewContentSize,分别用来记录ScrollView的自身尺寸和ScrollView的内容尺寸。

第一个ChildSizeReader包裹ScrollView,从而计算ScrollView的自身尺寸。

第二个ChildSizeReader包裹了一个VStack,里面进行了遍历加载视图,这样就计算了ScrollView的内容尺寸。

获得了两个尺寸,现在还差一个滑动的偏移量了,这里打算用PreferenceKey来进行记录。关于PreferenceKey的使用介绍,后期的文章会介绍。

  1. private struct ScrollOffsetPreferenceKey: PreferenceKey {
  2. static var defaultValue = CGFloat.zero
  3. static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
  4. value += nextValue()
  5. }
  6. }

现在将上面的demo改一下:

  1. struct ContentView: View {
  2. @State var scrollViewSize: CGSize = .zero
  3. @State var scrollViewContentSize: CGSize = .zero
  4. @Namespace var scrollViewSpace
  5. var body: some View {
  6. VStack {
  7. ChildSizeReader(size: $scrollViewSize) {
  8. ScrollView(.vertical, showsIndicators: false) {
  9. ChildSizeReader(size: $scrollViewContentSize) {
  10. VStack {
  11. ForEach(0..<30, id: \.self) { index in
  12. ......
  13. }
  14. }
  15. .background(GeometryReader {
  16. Color.clear.preference(key: ScrollOffsetPreferenceKey.self, value: -$0.frame(in: .named(scrollViewSpace)).minY)
  17. })
  18. .onPreferenceChange(ScrollOffsetPreferenceKey.self) { value in
  19. if value >= scrollViewContentSize.height - scrollViewSize.height {
  20. print("reached bottom.")
  21. }
  22. }
  23. }
  24. }
  25. .coordinateSpace(name: scrollViewSpace)
  26. }
  27. }
  28. }
  29. }

上面代码中首先添加了一个属性:@Namespace var scrollViewSpace,用来标识ScrollView的空间坐标。

再给VStack添加一个background,里面向ScrollOffsetPreferenceKey进行传值。在ScrollView不断滚动的时候,ScrollOffsetPreferenceKey里的reduce函数不断进行计算偏移量。

最后添加一个onPreferenceChange,得到ScrollOffsetPreferenceKey的值,这样通过文章开头说的公式计算一下,就知道什么时候滑动到底了。

别忘了还要给ScrollView添加一个coordinateSpace哦,因为我们要计算基于ScrollView坐标的偏移量。

文章比较简单,介绍了一种判断ScrollView滑动到底部的方法,当然还有其他方法,比如用LazyVStack组件等。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/代码探险家/article/detail/780888
推荐阅读
相关标签
  

闽ICP备14008679号