This commit is contained in:
Daniel Arantes Loverde
2026-04-16 14:50:07 -03:00
parent 7f7d414e6c
commit 0ebb854213
20 changed files with 871 additions and 216 deletions

View File

@@ -1,76 +1,35 @@
import SwiftUI
#if canImport(UIKit)
import UIKit
#endif
enum HomeScrollCoordinateSpace {
static let name = "home-scroll"
}
#if os(iOS)
@MainActor
struct ScrollOffsetObserver: UIViewRepresentable {
let onOffsetChange: (CGFloat) -> Void
struct HomeScrollOffsetPreferenceKey: PreferenceKey {
static let defaultValue: CGFloat = 0
func makeUIView(context: Context) -> ScrollOffsetProbeView {
let view = ScrollOffsetProbeView()
view.onOffsetChange = onOffsetChange
return view
}
func updateUIView(_ uiView: ScrollOffsetProbeView, context: Context) {
uiView.onOffsetChange = onOffsetChange
uiView.attachIfNeeded()
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
}
@MainActor
final class ScrollOffsetProbeView: UIView {
var onOffsetChange: (CGFloat) -> Void = { _ in }
struct ScrollOffsetObserver: View {
let onOffsetChange: (CGFloat) -> Void
private weak var observedScrollView: UIScrollView?
private var observation: NSKeyValueObservation?
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .clear
isUserInteractionEnabled = false
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
deinit {
observation?.invalidate()
}
override func didMoveToSuperview() {
super.didMoveToSuperview()
attachIfNeeded()
}
override func didMoveToWindow() {
super.didMoveToWindow()
attachIfNeeded()
}
func attachIfNeeded() {
guard let scrollView = findEnclosingScrollView() else { return }
guard scrollView !== observedScrollView else { return }
observation?.invalidate()
observedScrollView = scrollView
observation = scrollView.observe(\.contentOffset, options: [.initial, .new]) { [weak self] sv, _ in
self?.onOffsetChange(sv.contentOffset.y)
}
}
private func findEnclosingScrollView() -> UIScrollView? {
var view: UIView? = self
while let current = view {
if let scrollView = current as? UIScrollView {
return scrollView
var body: some View {
Color.clear
.frame(height: 0)
.background(
GeometryReader { geometry in
Color.clear.preference(
key: HomeScrollOffsetPreferenceKey.self,
value: geometry.frame(in: .named(HomeScrollCoordinateSpace.name)).minY
)
}
)
.onPreferenceChange(HomeScrollOffsetPreferenceKey.self) { minY in
onOffsetChange(-minY)
}
view = current.superview
}
return nil
}
}
#endif