Skip to content

Instantly share code, notes, and snippets.

@tovkal
Created March 16, 2022 15:34
Show Gist options
  • Save tovkal/5783c528c12eb90c28d8249fa90775c4 to your computer and use it in GitHub Desktop.
Save tovkal/5783c528c12eb90c28d8249fa90775c4 to your computer and use it in GitHub Desktop.
Find the bottom of a ScrollView in SwiftUI
import SwiftUI
struct BottomFindableScrollView<Content: View>: View {
private let axes: Axis.Set
private let showsIndicators: Bool
@Binding private var bottomReached: Bool
private let content: Content
private let coordinateSpaceName = "scrollSize"
@State private var wholeSize: CGSize = .zero
@State private var scrollViewSize: CGSize = .zero
init(
axes: Axis.Set = .vertical,
showsIndicators: Bool = true,
bottomReached: Binding<Bool>,
@ViewBuilder content: () -> Content
) {
self.axes = axes
self.showsIndicators = showsIndicators
self._bottomReached = bottomReached
self.content = content()
}
var body: some View {
ChildSizeReader(size: $wholeSize) {
ScrollView {
ChildSizeReader(size: $scrollViewSize) {
content
.background(GeometryReader { proxy in
Color.clear.preference(
key: ViewOffsetKey.self,
value: -1 * proxy.frame(in: .named(coordinateSpaceName)).origin.y
)
})
.onPreferenceChange(ViewOffsetKey.self, perform: { value in
bottomReached = value >= scrollViewSize.height - wholeSize.height
})
}
}
.coordinateSpace(name: coordinateSpaceName)
}
}
}
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
@ViewBuilder 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) {}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment