如何检测用户何时到达 ScrollView 的底部? [英] How do I detect when User has reached the bottom of the 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屋!