如何检测用户何时到达 ScrollView 的底部? [英] How do I detect when User has reached the bottom of the ScrollView?

查看:28
本文介绍了如何检测用户何时到达 ScrollView 的底部?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用以下代码作为参考:

I used the following code as a reference:

我认为它非常接近.似乎可以通过使用 origin.maxY 而不是 origin.y 来解决,但是 origin.maxY 似乎没有在 GeometryReader 中提供(严格来说:CGRect).

I think it's pretty close. It seems like it could probably be solved by using origin.maxY instead of origin.y, but origin.maxY doesn't seem to be provided in GeometryReader(strictly speaking: CGRect).

如何检测用户何时到达 ScrollView 的底部?

How do I detect when User has reached the bottom of the ScrollView?

import SwiftUI

struct ContentView: View {
  let spaceName = "scroll"

  @State var scrollViewSize: CGSize = .zero

  var body: some View {
    ScrollView {
      ChildSizeReader(size: $scrollViewSize) {
        VStack {
          ForEach(0..<100) { i in
            Text("\(i)")
          }
        }
        .background(
          GeometryReader { proxy in
            Color.clear.preference(
              key: ViewOffsetKey.self,
              value: -1 * proxy.frame(in: .named(spaceName)).origin.y
            )
          }
        )
        .onPreferenceChange(
          ViewOffsetKey.self,
          perform: { value in
            print("offset: \(value)") // offset: 1270.3333333333333 when User has reached the bottom
            print("height: \(scrollViewSize.height)") // height: 2033.3333333333333

            if value == scrollViewSize.height {
              print("User has reached the bottom of the ScrollView.")
            } else {
              print("not reached.")
            }
          }
        )
      }
    }
    .coordinateSpace(name: spaceName)
    .onChange(
      of: scrollViewSize,
      perform: { value in
        print(value)
      }
    )
  }
}

struct ViewOffsetKey: PreferenceKey {
  typealias Value = CGFloat
  static var defaultValue = CGFloat.zero
  static func reduce(value: inout Value, nextValue: () -> Value) {
    value += nextValue()
  }
}

struct ChildSizeReader<Content: View>: View {
  @Binding var size: CGSize

  let content: () -> Content
  var body: some View {
    ZStack {
      content().background(
        GeometryReader { proxy in
          Color.clear.preference(
            key: SizePreferenceKey.self,
            value: proxy.size
          )
        }
      )
    }
    .onPreferenceChange(SizePreferenceKey.self) { preferences in
      self.size = preferences
    }
  }
}

struct SizePreferenceKey: PreferenceKey {
  typealias Value = CGSize
  static var defaultValue: Value = .zero

  static func reduce(value _: inout Value, nextValue: () -> Value) {
    _ = nextValue()
  }
}

推荐答案

把你的整个 ScrollView 包裹在你的 ChildSizeReader 中,这样你就可以得到 的高度ScrollView 本身.

Wrap your whole ScrollView in your ChildSizeReader, so you can get the height of the ScrollView itself.

因为偏移量从顶部的零开始,当在滚动视图的底部时,结束不是在屏幕的顶部,而是底部.这种差异是滚动视图的高度.这意味着 ScrollView 从偏移量 0 开始并转到 total content height - scroll view height.

Because the offset starts at zero at the top, when at the bottom of the scroll view the end isn't at the top of the screen, but rather the bottom. This difference is the height of the scroll view. This means the ScrollView starts at offset 0 and goes to total content height - scroll view height.

代码:

struct ContentView: View {
    let spaceName = "scroll"

    @State var wholeSize: CGSize = .zero
    @State var scrollViewSize: CGSize = .zero

    var body: some View {
        ChildSizeReader(size: $wholeSize) {
            ScrollView {
                ChildSizeReader(size: $scrollViewSize) {
                    VStack {
                        ForEach(0..<100) { i in
                            Text("\(i)")
                        }
                    }
                    .background(
                        GeometryReader { proxy in
                            Color.clear.preference(
                                key: ViewOffsetKey.self,
                                value: -1 * proxy.frame(in: .named(spaceName)).origin.y
                            )
                        }
                    )
                    .onPreferenceChange(
                        ViewOffsetKey.self,
                        perform: { value in
                            print("offset: \(value)") // offset: 1270.3333333333333 when User has reached the bottom
                            print("height: \(scrollViewSize.height)") // height: 2033.3333333333333

                            if value >= scrollViewSize.height - wholeSize.height {
                                print("User has reached the bottom of the ScrollView.")
                            } else {
                                print("not reached.")
                            }
                        }
                    )
                }
            }
            .coordinateSpace(name: spaceName)
        }
        .onChange(
            of: scrollViewSize,
            perform: { value in
                print(value)
            }
        )
    }
}

注意您已经存在的 scrollViewSize 变量是内容的大小,而不是滚动视图的大小.

Note your already existing scrollViewSize variable is the content's size, not the scroll view's size.

另请注意,我将 == 更改为 >= - 这样您就不必完全处于高度,可以过度滚动它用橡皮筋固定回来.

Also notice that I changed the == to >= - this is so you don't have to be exactly at the height, can be over-scrolled where it rubber-bands back.

这篇关于如何检测用户何时到达 ScrollView 的底部?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆