forked from AcaMate/AcaMate_iOS
75 lines
2.5 KiB
Swift
75 lines
2.5 KiB
Swift
//
|
|
// OffsetObservableScollView.swift
|
|
// AcaMate
|
|
//
|
|
// Created by TAnine on 2/10/25.
|
|
//
|
|
|
|
import SwiftUI
|
|
import Combine
|
|
|
|
struct OffsetObservableScrollView<Content: View>: View {
|
|
var axes: Axis.Set = .vertical
|
|
var showsIndicators: Bool = true
|
|
|
|
@Binding var scrollOffset: CGPoint
|
|
@ViewBuilder var content: (ScrollViewProxy) -> Content
|
|
|
|
@Namespace var coordinateSpaceName: Namespace.ID
|
|
|
|
init(
|
|
_ axes: Axis.Set = .vertical,
|
|
showsIndicators: Bool = true,
|
|
scrollOffset: Binding<CGPoint>,
|
|
@ViewBuilder content: @escaping (ScrollViewProxy) -> Content
|
|
) {
|
|
self.axes = axes
|
|
self.showsIndicators = showsIndicators
|
|
self._scrollOffset = scrollOffset
|
|
self.content = content
|
|
}
|
|
|
|
var body: some View {
|
|
ScrollView(axes, showsIndicators: showsIndicators) {
|
|
ScrollViewReader { scrollViewProxy in
|
|
VStack(spacing: 0) {
|
|
// 맨 위로 스크롤하기 위한 anchor 뷰 (id "TOP")
|
|
Color.clear
|
|
.frame(height: 1)
|
|
.id("TOP")
|
|
|
|
// 실제 콘텐츠. content 클로저에서 scrollViewProxy를 전달받으므로,
|
|
// 버튼 등을 통해 프로그래밍적으로 스크롤할 수 있음.
|
|
content(scrollViewProxy)
|
|
.overlay {
|
|
GeometryReader { geometryProxy in
|
|
Color.clear
|
|
.preference(
|
|
key: ScrollOffsetPreferenceKey.self,
|
|
value: CGPoint (
|
|
x: -geometryProxy.frame(in: .named(coordinateSpaceName)).minX,
|
|
y: -geometryProxy.frame(in: .named(coordinateSpaceName)).minY
|
|
)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.coordinateSpace(name: coordinateSpaceName)
|
|
.onPreferenceChange(ScrollOffsetPreferenceKey.self) { value in
|
|
printLog("\(scrollOffset) -> \(value)")
|
|
scrollOffset = value
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
struct ScrollOffsetPreferenceKey: PreferenceKey {
|
|
static var defaultValue: CGPoint = .zero
|
|
|
|
static func reduce(value: inout CGPoint, nextValue: () -> CGPoint) {
|
|
value = nextValue()
|
|
}
|
|
}
|