SwiftUI - ScrollViewReader

이재원·2024년 7월 3일
0

SwiftUI

목록 보기
2/9
post-thumbnail
post-custom-banner

ScrollViewReader

ScrollViewReader는 스크롤링이 감지되거나 자동으로 위치 이동이 필요할 때 사용하는 뷰로써, ScrollViewProxy라는 것을받아서 스크롤 기능을 수행할 수 있도록 해주는 뷰입니다.

ScrollViewReader의 선언

ScrollViewReader 사용

@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)
}

위의 예제는 공식문서에 작성되어 있는 예제 코드입니다.

ScrollViewReader는 proxy를 받고, 이 proxy는 scrollTo라는 것을 프로퍼티로 갖고 있는 것을 알 수 있습니다.

그리고 srrollTo() 메서드에 각 button의 id값을 넘겨주는 것을 알 수 있는데, 이 작업을 통해 버튼을 클릭시 자동으로 스크롤 되는 것을 알 수 있습니다.

여기서 중요한 점은 ScrollViewProxy를 컨텐트 뷰 빌더가 실행되는 동안에 사용하면 안됩니다. 컨텐트 뷰 빌더가 실행하는 도중에 사용하게 되면 런타임 에러를 발생할 것입니다. 대신에, 제스처 핸들러나 onChange 메서도와 같이 컨텐트 내에서 생성된 액션에 대해서만 프록시를 호출 할 수 있습니다.

struct ContentView: View {
    @Namespace var topID
    @Namespace var bottomID
    @State var isTapped: Bool = false
    
    var body: some View {
        ScrollViewReader { proxy in
            ScrollView {
                Button("Scroll to Bottom") {
                    withAnimation {
//                        isTapped.toggle()
                        proxy.scrollTo(bottomID)
                    }
                }
                .id(topID)
                
                VStack(spacing: 0) {
                    ForEach(0..<100) { i in
                        color(fraction: Double(i) / 100)
                            .frame(height: 32)
                        
                        if isTapped {
                            VStack {
                                Text("이상한걸 보여드리겠습니다.")
                                Text("눈뜨고 지켜봐주세요!")
                                Text("버튼은 어디에..?")
                            }
                        }
                    }
                }
                
                Button("Top") {
                    withAnimation {
                        proxy.scrollTo(topID)
                    }
                }
                .id(bottomID)
            }
        }
    }
}

func color(fraction: Double) -> Color {
    Color(red: fraction, green: 1 - fraction, blue: 0.5)
}

이와 같이 scrollViewProxy가 실행되고 있는 도중에 VStack이라는 새로운 뷰를 그리려 하니 뷰가 깨지는 것을 볼 수 있습니다.

이는 LazyVStack을 사용하면 해결이 가능합니다.

LazyVStack(spacing: 0) {
  ForEach(0..<100) { i in
    color(fraction: Double(i) / 100)
      .frame(height: 32)
    
    if isTapped {
      VStack {
        Text("이상한걸 보여드리겠습니다.")
        Text("눈뜨고 지켜봐주세요!")
        Text("버튼은 어디에..?")
      }
    }
  }
}

ScrollViewProxy

ScrollViewProxy는 구조체입니다. 직접적으로 인스턴스를 생성하지 않고, 컨텐트 뷰 빌더 안에서 받습니다. 그리고 이 안에 scrollTo 메서드가 존재합니다.

scrollTo

scrollTo의 기본적인 선언은 위와 같습니다.

위치를 이동시킬 id와 어디로 이동할지를 알기 위한 anchor가 매개변수로 있습니다.

anchor을 따로 설정해 주지 않는다면 nil로 지정이 되고, 각 아이디의 위치에 알맞게 이동하게 됩니다.

출처
https://green1229.tistory.com/295
https://developer.apple.com/documentation/swiftui/scrollviewreader https://developer.apple.com/documentation/swiftui/scrollviewproxy https://developer.apple.com/documentation/swiftui/scrollviewproxy/scrollto(_:anchor:)

profile
20학번 새내기^^(였음..)
post-custom-banner

0개의 댓글