Created
March 16, 2022 15:34
-
-
Save tovkal/5783c528c12eb90c28d8249fa90775c4 to your computer and use it in GitHub Desktop.
Find the bottom of a ScrollView in SwiftUI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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