안녕하세요.
SwiftUI의 ScrollView를 사용할 때 유용한 기능을 사용할 수 있는 ScrollViewReader와 ScrollViewProxy에 대해 알아보겠습니다.
1️⃣ ScrollViewReader

iOS 14.0+부터 사용 가능한 ScrollViewReader는 ScrollViewProxy를 전달하여 ScrollView 내에서 특정 뷰로의 프로그래밍적 스크롤을 가능하게 하는 컨테이너입니다. GeometryProxy를 전달하는 GeometryReader와 유사하죠?
ScrollViewProxy는 ScrollViewReader 내부에서만 접근할 수 있습니다.
ScrollViewReader { proxy in
ScrollView {
VStack {
// 스크롤 가능한 콘텐츠
}
}
}

이제 저 proxy를 써봅시다!
2️⃣ScrollViewProxy

ScrollViewProxy란?
마찬가지로 iOS 14.0+에서 추가되었으며 ScrollViewReader 내에서 제공되는 프로토콜로, 사용자의 입력 없이도 ScrollView 내부의 특정 요소로 프로그래밍적 스크롤을 가능하게 합니다.
위 기능을 제공하는 메서드가 바로 ScrollViewProxy에서 제공하는 scrollTo(_:) 메서드입니다.
3️⃣scrollTo(_:anchor:)
scrollTo 메서드는 다음과 같은 형태를 가집니다:

- id: 스크롤할 대상의 ID(Hashable 프로토콜을 준수해야 함)
- anchor: 스크롤할 때 정렬할 기준점(기본값은 nil이며, .top, .center, .bottom 등 설정 가능)
😊 아래 예제에서는 Button을 클릭하면 특정 행으로 스크롤하는 기능을 구현합니다.
@Namespace var topID
@Namespace var bottomID
var body: some View {
ScrollViewReader { proxy in
ScrollView {
Button("Scroll to Bottom") {
withAnimation {
proxy.scrollTo(bottomID)
}
}
.id(topID)
VStack(spacing: 0) {
ForEach(0..<100) { i in
color(fraction: Double(i) / 100)
.frame(height: 32)
}
}
Button("Top") {
withAnimation {
proxy.scrollTo(topID)
}
}
.id(bottomID)
}
}
}
func color(fraction: Double) -> Color {
Color(red: fraction, green: 1 - fraction, blue: 0.5)
}
‼️주의해야 할 점
- ID가 존재해야 한다: scrollTo 메서드는 id를 기준으로 작동하므로, 스크롤할 대상에 id(_:)를 명시적으로 설정해야 합니다.
- 뷰가 렌더링된 후에 호출해야 한다: 만약 특정 위치로 스크롤이 된 상태로 화면이 표시되어야 할 때, 뷰가 생성되기 전에 호출하면 안됩니다. 만약 이런 경우 onAppear 내부에서 DispatchQueue.main.async를 사용하여 호출하는 것이 좋습니다.
.onAppear {
DispatchQueue.main.async {
proxy.scrollTo(someId, anchor: .center)
}
}