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


    最近开发的项目中遇到一个需求,要在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组件等。

  • 相关阅读:
    软考 系统架构设计师系列知识点之边缘计算(5)
    springboot毕设项目宠物咖啡馆系统的设计与实现ok8a3(java+VUE+Mybatis+Maven+Mysql)
    马斯克调整了Twitter API服务方案
    【生成对抗网络】
    STM32F4_HAL库_串口阻塞/中断/DMA三种方式发送数据的配置
    Git基础(21):GitLab创建组、用户、项目
    GCC Rust获批将被纳入主线代码库,或将于GCC 13中与大家见面
    面试二十一、红黑树
    SBD(Schottky Barrier Diode)与JBS(Junction Barrier Schottky)
    Java高级特性-泛型方法
  • 原文地址:https://blog.csdn.net/guoyongming925/article/details/132853536