赞
踩
最近开发的项目中遇到一个需求,要在ScrollView滑动到底部的时候显示一个loading,然后请求第二页数据。这就涉及到怎么判断ScrollView滑动到底部了。
那么可以用这面这个条件判断:
偏移量 >= (ScrollView内容高度 - ScrollView自身高度)
在用这个判断条件之前,得先获得到ScrollView内容高度和ScrollView自身高度,先介绍个小组件:ChildSizeReader
- 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
- }
- }
- }
-
- private struct SizePreferenceKey: PreferenceKey {
- typealias Value = CGSize
- static var defaultValue: Value = .zero
- static func reduce(value _: inout Value, nextValue: () -> Value) {
- _ = nextValue()
- }
- }
ChildSizeReader组件接受两个值,size和content,这里的size用了@Binding修饰,需要调用的地方传个引用过来。content就不用多说了。举个例子用一下:
- struct ContentView: View {
-
- @State var scrollViewSize: CGSize = .zero
- @State var scrollViewContentSize: CGSize = .zero
-
- var body: some View {
- VStack {
- ChildSizeReader(size: $scrollViewSize) {
- ScrollView(.vertical, showsIndicators: false) {
- ChildSizeReader(size: $scrollViewContentSize) {
- VStack {
- ForEach(0..<30, id: \.self) { index in
- VStack {
- Text("This is the \(index)th line.")
- .padding()
- .frame(height: 30)
- .frame(maxWidth: .infinity, alignment: .leading)
- Color.gray
- .frame(height: 1)
- }
- }
- }
- .onAppear {
- print("scrollViewSize: \(scrollViewSize)")
- print("scrollViewContentSize: \(scrollViewContentSize)")
- }
- }
- }
- }
- }
- }
- }
上面代码中首先定义了两个属性,scrollViewSize和scrollViewContentSize,分别用来记录ScrollView的自身尺寸和ScrollView的内容尺寸。
第一个ChildSizeReader包裹ScrollView,从而计算ScrollView的自身尺寸。
第二个ChildSizeReader包裹了一个VStack,里面进行了遍历加载视图,这样就计算了ScrollView的内容尺寸。
获得了两个尺寸,现在还差一个滑动的偏移量了,这里打算用PreferenceKey来进行记录。关于PreferenceKey的使用介绍,后期的文章会介绍。
- private struct ScrollOffsetPreferenceKey: PreferenceKey {
- static var defaultValue = CGFloat.zero
- static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
- value += nextValue()
- }
- }
现在将上面的demo改一下:
- struct ContentView: View {
-
- @State var scrollViewSize: CGSize = .zero
- @State var scrollViewContentSize: CGSize = .zero
- @Namespace var scrollViewSpace
-
- var body: some View {
- VStack {
- ChildSizeReader(size: $scrollViewSize) {
- ScrollView(.vertical, showsIndicators: false) {
- ChildSizeReader(size: $scrollViewContentSize) {
- VStack {
- ForEach(0..<30, id: \.self) { index in
- ......
- }
- }
- .background(GeometryReader {
- Color.clear.preference(key: ScrollOffsetPreferenceKey.self, value: -$0.frame(in: .named(scrollViewSpace)).minY)
- })
- .onPreferenceChange(ScrollOffsetPreferenceKey.self) { value in
- if value >= scrollViewContentSize.height - scrollViewSize.height {
- print("reached bottom.")
- }
- }
- }
- }
- .coordinateSpace(name: scrollViewSpace)
- }
- }
- }
- }
上面代码中首先添加了一个属性:@Namespace var scrollViewSpace,用来标识ScrollView的空间坐标。
再给VStack添加一个background,里面向ScrollOffsetPreferenceKey进行传值。在ScrollView不断滚动的时候,ScrollOffsetPreferenceKey里的reduce函数不断进行计算偏移量。
最后添加一个onPreferenceChange,得到ScrollOffsetPreferenceKey的值,这样通过文章开头说的公式计算一下,就知道什么时候滑动到底了。
别忘了还要给ScrollView添加一个coordinateSpace哦,因为我们要计算基于ScrollView坐标的偏移量。
文章比较简单,介绍了一种判断ScrollView滑动到底部的方法,当然还有其他方法,比如用LazyVStack组件等。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。