← 아카이브로 돌아가기

Development · SwiftUI

ScrollViewReader로 스크롤 위치 제어하기

뷰에 ID를 부여하고 scrollTo를 호출해 원하는 항목으로 이동하는 방법과 anchor 옵션을 정리합니다.

Velog 원문 보기 ↗

Development 아카이브 표지

긴 목록에서 특정 항목으로 바로 이동하려면 스크롤 거리보다 대상의 식별자가 필요하다. ScrollViewReader가 내부의 뷰를 찾고 위치를 옮기는 방법을 안과 밖의 호출로 나누어 봤다.

ScrollView 내부에서 제어

일반적으로 ScrollView 내부의 포함되는 뷰의 식별자를 사용하여 특정 뷰로 스크롤할 수 있다. id값을 지정하여 해당 위치의 뷰로 스크롤할 수 있고, anchor를 통해 스크롤된 뷰의 위치를 지정할 수 있다.

ScrollView {
    ScrollViewReader { proxy in
        Button("Click here to go to #49") {
            withAnimation(.spring()) {
            	// 특정 id값의 위치로 스크롤
                proxy.scrollTo(30, anchor: .center)
            }
        }
        ForEach(0..<50) { index in
            Text("This is item #\(index)")
                .font(.headline)
                .frame(height: 200)
                .frame(maxWidth: .infinity)
                .background(Color.white)
                .cornerRadius(16)
                .shadow(radius: 10)
                .padding()
                .id(index) // id값 생성
        }
    }
}

ScrollView 외부에서 제어

TextField와 onChange를 활용하여 ScrollView 외부에서 스크롤 위치를 제어할 수 있다.

struct ScrollViewReaderStudy: View {
    
    @State var scrollToIndex: Int = 0
    @State var textFieldText: String = ""
    
    var body: some View {
        VStack {
        	// String 타입으로 스크롤할 ScrollView의 특정 위치를 입력
            TextField("scroll number", text: $textFieldText)
                .frame(height: 56)
                .border(.gray)
                .padding(.horizontal)
                .keyboardType(.numberPad)
            Button("Scroll Now") {
            	// Int 타입으로 입력받은 위치를 변환
                if let index = Int(textFieldText) {
                    scrollToIndex = index
                }
            }
            ScrollView {
                ScrollViewReader { proxy in
                    ForEach(0..<50) { index in
                        Text("This is item #\(index)")
                            .font(.headline)
                            .frame(height: 200)
                            .frame(maxWidth: .infinity)
                            .background(Color.white)
                            .cornerRadius(16)
                            .shadow(radius: 10)
                            .padding()
                            .id(index)
                    }
                    // 입력받은 Int 타입의 위치 값이 변할 때 해당 위치로 스크롤
                    .onChange(of: scrollToIndex) { newValue in
                        withAnimation {
                            proxy.scrollTo(newValue, anchor: .center)
                        }
                    }
                }
            }
        }
    }
}

← 아카이브로 돌아가기