[SwiftUI] 코드를 통해 스크롤링을 작동시키는 방법 (programmatic scrolling)

Jaeho Yoo
6 min readMay 14, 2023

--

안녕하세요. 현대자동차 유재호 연구원입니다. 😌

아이폰의 물리적인 화면의 범위를 넘어가는 콘텐츠가 있을 때, 사용자들은 자연스럽게 스크롤링(scrolling) 액션을 시도합니다.

Fun Fact) 손가락을 통한 스크롤링은 아이폰이 처음으로 도입했다

사용자가 ‘직접’ 화면을 스크롤하는 액션은, 개발자가 신경쓰지 않아도 자동으로 이뤄집니다.

SwiftUI 에서는 기본적으로 스크롤 기능이 탑재된 컴포넌트들이 있습니다. 바로, ScrollView, List, Form 인데요.

이렇게 편리한 컴포넌트들이 존재하기 때문에, 요즘 앱개발에서 스크롤 기능을 만드는 것 자체는 아주 쉽습니다.

그런데, 사용자가 ‘직접’ 화면을 스크롤하는 게 아니라, 개발자가 의도적으로 스크롤을 작동시키는 로직(programmatic scrolling)은 간과되곤 합니다.

예를 들어서, 화면에 알록달록한 컬러칩을 쌓는 앱을 만들어볼게요.

화면의 엄지 부분에 있는 동그란 ➕버튼을 누르면, 랜덤한 컬러가 하나씩 추가되는 화면을 만들었습니다.

➕버튼을 누르면 컬러가 하나씩 쌓인다

어떤가요?

처음에는 부드럽게 느껴지다가, 컬러칩이 아이폰 화면에 가득 채워졌을 때를 잘 봐주세요.

➕버튼을 분명 눌렀으니, 지금까지 그랬듯이 새로운 컬러칩이 추가됐을 거라 ‘예상’ 은 할 수 있지만, 눈에 보이지 않으니 ‘확신’ 하기는 어렵겠죠.

새로운 컬러칩이 추가됐는지 확인하려면, 사용자가 ‘직접’ 스크롤 해야 하는 시나리오가 됐죠.

잘못된 기능은 아니지만, 불편한 UX 로 느껴집니다.

이제 여기서, programmatic scrolling 을 적용해보겠습니다.

개발자인 저는, 새로 추가된 컬러칩이 화면의 범위를 넘어간다면, 자동으로 스크롤이 내려가는 기능을 추가했습니다.

자동 스크롤을 통해 UX 가 크게 향상됐다

어떤가요?

컬러칩이 추가됨에 따라 자동으로 스크롤이 움직이기 때문에, 이제 사용자는 컬러칩이 새롭게 추가되고 있다는 사실을 ‘직관적으로’ 알아볼 수 있게 됐어요! 😄

그럼 이제 코드를 통해 살펴보죠.

먼저, SwiftUI 에서 programmatic scrolling 기능을 구현하려면 ScrollViewReader 를 사용해야 합니다. (iOS 14.0 이상에서 사용 가능)

ScrollViewReader 를 세팅하면, 아래엔 ViewBuilder 코드가 생기고, ScrollViewProxy 타입의 인스턴스가 하나 생깁니다.

ScrollViewProxy ?

굉장히 생소한 타입인데요, 간단하게 공식 문서를 확인해보면..

A proxy value that supports programmatic scrolling of the scrollable views

즉, 스크롤이 가능한 view 에서 프로그래밍 방식으로 스크롤을 작동시키기 위한 대리자(proxy value, delegate)입니다.

“proxy” 라고 이름 짓고, ViewBuilder 안에는 스크롤이 가능한 컴포넌트를 넣어줍니다. ScrollView, List, Form 이 들어갈 수 있습니다.

실험을 위해, 1 ~ 200 까지의 숫자가 나열된 스크롤뷰를 만들어보겠습니다.

이동할 대상에는 id 를 꼭 할당해줘야 합니다.

그리고 proxy 를 통해 scrollTo 메서드를 호출할 수 있습니다. 이 메서드의 인자로, 이동할 목표의 id 를 넣어주고, ‘anchor’ 를 설정해줍니다.

anchor 의 역할이 혼동하기 쉬운 부분인데요. 실험을 통해 알아보죠.

저는 숫자 100 으로 이동하려고 합니다.

이때, anchor 를 top center bottom 이렇게 세 가지 버튼을 각각 만들어서 어떻게 이동하는지 살펴보겠습니다.

anchor 인자 실험

위 gif 를 보시면, 형광펜 쳐진 숫자 100 으로 이동 할 때, anchor 가 top center bottom 중에 무엇인지에 따라, 이동이 끝났을 때 최종 위치가 결정되는 걸 알 수 있습니다.

anchor 가 bottom 이면, 타겟이 스크롤뷰의 bottom 에 위치하는 것이죠.

여기까진 이해하기 어렵지 않습니다. 근데 이런 경우가 있습니다.

스크롤뷰의 특정 컴포넌트로 이동하는 게 아니라, 그냥 스크롤뷰의 최상단으로 이동하는 scrollToTop 기능이 필요할 수 있겠죠.

반대로 스크롤뷰의 최하단으로 이동하는 scrollToBottom 기능도 필요할 수 있구요.

저는 이렇게 하고 있습니다.

scrollToTop 기능이 필요할 땐, 스크롤뷰 안에서 가장 ‘위’ 에 있는 컴포넌트에 고유한 id 를 줍니다.

위에 제가 만든 예시에선, 숫자 1 이 가장 위에 있죠?

그리고 anchor 인자로 top 이 아니라, bottom 을 줍니다.

그 이유는, scrollTo 메서드가 작동할 때, anchor 까지 ‘끌어당기려’ 는 성질이 있기 때문입니다.

따라서, 숫자 1 이 화면의 상단부(top)에 도착했다고 바로 멈추기 보다는, 하단부(bottom)까지 잡아당겨서, 최대한 스크롤뷰의 최상단으로 이동할 수 있게 하는 거죠.

실무에서 SwiftUI 로 개발하다 보면, 이런 꼼수(?)가 필요할 때가 있는데.. 예시로 만들려고 하니까 딱히 적절한 상황이 떠오르지가 않네요. 😅

이번 글은 여기서 마무리하겠습니다.

위 예시에 대한 전체 코드는 아래 링크를 확인해주세요. 감사합니다!

--

--