// // ScorllView_Example.swift // RememberbyAnything // // Created by Sean Kim on 5/14/24. // import SwiftUI struct ScorllView_Example: View { @StateObject private var scrollViewModel: ScrollViewModel = ScrollViewModel() @State var isStartScroll: Bool = true // scroll 위치 감지 변경하는 부분 private var scrollObservableView: some View { GeometryReader { proxy in let offsetY = proxy.frame(in: .global).origin.y Color.clear .preference( key: ScrollOffsetKey.self, value: offsetY ) .onAppear { scrollViewModel.setOriginOffset(offsetY) } } .frame(height: 0) } var body: some View { ScrollViewReader { scroll in ScrollView(showsIndicators: false) { scrollObservableView.id(-1) } .onPreferenceChange(ScrollOffsetKey.self) { scrollViewModel.setOffset($0) self.isStartScroll = scrollViewModel.isStartScroll } // 스크롤이 시작할 경우 최상단으로 올리는 버튼 그리기 .overlay(alignment: .top) { if isStartScroll { GeometryReader { geo in Button { withAnimation { scroll.scrollTo(-1, anchor: .center) } } label: { Icon.up .font(.nps(size: 30)) .foregroundStyle(.brandDeepBlue) .frame(width: geo.size.width, height: isStartScroll ? 30 : 0,alignment: .center) .padding(.init(top: 10, leading: 0, bottom: 20, trailing: 0)) .background( LinearGradient( colors: [.whiteSora, .whiteSora.opacity(0)], startPoint: .top, endPoint: .bottom) ) } } } } } } } // MARK: - 스크롤 뷰 관련 modifier struct HeightPreferenceKey: PreferenceKey { static var defaultValue: CGFloat = 0 static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { value = nextValue() } } // 자식뷰에서 상위 뷰로 데이터 전달시 사용 = PreferenceKey struct ScrollOffsetKey: PreferenceKey { static var defaultValue: CGFloat = .zero static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { value += nextValue() } } final class ScrollViewModel: ObservableObject { @Published var offset: CGFloat = 0 @Published var isStartScroll: Bool = false var originOffset: CGFloat = 0 var isCheckedOriginOffset: Bool = false func setOriginOffset(_ offset: CGFloat) { guard !isCheckedOriginOffset else { return } self.originOffset = offset - 30 // 스크롤 패딩 넣은거 만큼 빼야 함 self.offset = offset isCheckedOriginOffset = true } func setOffset(_ offset: CGFloat) { self.offset = offset if self.offset < self.originOffset { isStartScroll = true } else { isStartScroll = false } } }