반응형
아이폰 개발 시 스크롤 뷰를 사용하면 델리게이터를 이용해서 스크롤의 오프셋 값을 쉽게 알 수 있습니다. 하지만 스위프트 UI를 사용하면 어떻게 가져올지 막막합니다. 특정 위치에 스크롤을 강제로 하로 싶을 때 ScrollViewReader를 사용해서 이동시킬 수는 있지만 Offset값을 실시간으로 알기 위해서는 PreferenceKey를 통해서 값을 값을 실시간으로 추적해야 합니다.
PreferenceKey 프로토콜 구현
우선 위의 PreferenceKey를 구현해보겠습니다. defaultValue와 reduce 함수를 정의해줍니다.
struct ScrollPreferenceKey: PreferenceKey {
static var defaultValue: CGFloat = .zero
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value += nextValue()
}
}
ScrollView UI 생성
이제 뷰를 만들어주겠습니다. 하나의 스크롤 뷰와 하단의 스크롤 위치 값을 표현하기 위한 Text를 위치시킵니다.
struct ContentView: View {
@State private var offsetY: CGFloat = .zero
var body: some View {
VStack {
ScrollView {
VStack(spacing: 0) {
LazyVStack(spacing: 0) {
ForEach(0...100, id: \.self) { item in
ZStack() {
Color.gray
Text(String(item))
}
}
}
}
}
Text("\(offsetY)")
.padding()
}
.clipped()
}
}
hiddenView 생성
이제 값을 추적할 하나의 화면을 만들어줍니다. 이름은 hiddenView로 해주고 GeometryReader를 통해서 오프셋 값의 y를 이전 화면에서 @State 로 정의한 offsetY에 대입해 줍니다. 이 화면은 View로 작성하는 것보다 백그라운드에 추가하는 편이 좋을 수도 있지만 필요 없을 때는 쉽게 제거할 수 있도록 View로 작성합니다.
private var hiddenView: some View {
GeometryReader { proxy in
let offsetY = proxy.frame(in: .global).origin.y
Color.clear
.preference(
key: ScrollPreferenceKey.self,
value: offsetY
)
.onAppear { // 나타날때 뷰의 최초위치를 저장하는 로직
self.offsetY = offsetY
}
}
.frame(height: 0)
}
Offset 값 추적하기
마지막으로 좀 전에 만든 hiddenView를 화면에 삽입하고 onPreferenceChange 이벤트로 값을 추적합니다.
var body: some View {
VStack {
ScrollView {
VStack(spacing: 0) {
hiddenView
LazyVStack(spacing: 0) {
ForEach(0...100, id: \.self) { item in
ZStack() {
Color.gray
Text(String(item))
}
}
}
}
}
.clipped()
.onPreferenceChange(ScrollPreferenceKey.self, perform: { value in
self.offsetY = value
})
Text("\(offsetY)")
.padding()
}
.clipped()
}
전체코드
import SwiftUI
struct ContentView: View {
@State private var offsetY: CGFloat = .zero
private var hiddenView: some View {
GeometryReader { proxy in
let offsetY = proxy.frame(in: .global).origin.y
Color.clear
.preference(
key: ScrollPreferenceKey.self,
value: offsetY
)
.onAppear { // 나타날때 뷰의 최초위치를 저장하는 로직
self.offsetY = offsetY
}
}
.frame(height: 0)
}
var body: some View {
VStack {
ScrollView {
VStack(spacing: 0) {
hiddenView
LazyVStack(spacing: 0) {
ForEach(0...100, id: \.self) { item in
ZStack() {
Color.gray
Text(String(item))
}
}
}
}
}
.clipped()
.onPreferenceChange(ScrollPreferenceKey.self, perform: { value in
self.offsetY = value
})
Text("\(offsetY)")
.padding()
}
.clipped()
}
}
struct ScrollPreferenceKey: PreferenceKey {
static var defaultValue: CGFloat = .zero
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value += nextValue()
}
}
#Preview {
ContentView()
}
반응형
'언어 > 스위프트 UI' 카테고리의 다른 글
[SwiftUI] 버튼 활성 / 비활성 처리하기 (0) | 2024.02.22 |
---|---|
[SwiftUI] ScrollView 중앙에 위치 시키기 (0) | 2024.02.16 |
[SwiftUI] URLSession 통신 await async JSON 데이터 로딩 처리하기 (0) | 2024.01.18 |
[SwiftUI] 네비게이션 바 버튼 만들기 (0) | 2023.07.10 |
[SwiftUI] URLSession을 이용한 간단한 JSON 통신 및 데이터 파싱 (0) | 2023.03.21 |