iyOmSd/Title: SwiftUI

[SwiftUI] WWDC23 Beyond scroll views

냄수 2024. 11. 23. 16:22
반응형

Margins and safe area

ScrollView에 패딩을 넣으면 ScrollView가 인셋되긴 하지만 스크롤할 때 콘텐츠가 잘림

ScrollView 자체를 인셋하는 대신 ScrollView의 콘텐츠 여백을 확장하려고 합니다

새로 생긴 safeAreaPadding 수정자를 쓰면 됩니다.

이건 일반 padding 수정자와 똑같이 동작하기는 하지만 콘텐츠가 아니라 안전 영역에 패딩을 넣기 때문에 ScrollView가 전체 너비에 적용되어 다음 아이템이 살짝 엿보이게 됩니다

 

safe area는 앱이 작동하는 기기에 주로 있고 safeAreaPadding이나 safeAreaInset 수정자 같은 API에도 있을 수 있습니다 

ScrollView는 safe area를 분할해 콘텐츠에 적용하는 여백으로 만듭니다

여기에는 여러분이 담당하는 콘텐츠도 포함되지만 ScrollView가 담당하는 추가 콘텐츠도 포함됩니다

예로 스크롤 인디케이터가 있습니다.

즉, safe area를 변경함으로써 다양한 콘텐츠에 대응하는 다양한 인셋을 설정하는 건 불가능하다는 뜻입니다

 

 

[contentMargins]

인셋을 각기 다르게 적용하고 싶다면 새로 생긴 contentMargins API를 사용하면 됩니다 

이 API로 ScrollView의 콘텐츠를 스크롤 인디케이터와 별도로 인셋할 수 있음

혹은 인디케이터를 콘텐츠와 별도로 인셋할 수도 있음

 

 

Targets and positions

 

기본적으로 ScrollView는 표준 감속률과 스크롤 속도를 함께 사용하여 스크롤이 끝나야 할 타깃 콘텐츠 오프셋을 계산합니다

ScrollView의 크기나 콘텐츠는 고려하지 않습니다

하지만 그런 게 중요할 때도 있습니다

 

최신 SwiftUI에서는 scrollTargetBehavior 수정자를 사용해서 ScrollView가 타깃 콘텐츠 오프셋을 계산하는 방식을 바꿀 수 있습니다 

이 수정자가 사용하는 타입은 scrollTargetBehavior 프로토콜을 준수합니다

paging 동작을 지정하면 ScrollView는 한 번에 한 페이지씩 넘어갑니다

이 페이징 동작은 특수합니다

감속률을 사용자가 지정했고 ScrollView 자체의 컨테이너 크기에 맞춰 어디로 스크롤할지 정함

iOS에서는 이게 효과적이지만 화면이 큰 iPadOS에서는 다소 과한 느낌이 듭니다

 

viewAligned 동작은 뷰에 맞춰 ScrollView를 정렬하므로 ScrollView는 어떤 뷰를 정렬 기준으로 삼아야 할지를 알아야 합니다

이러한 뷰를 스크롤 타깃이라고 하는데 특정 뷰를 스크롤 타깃으로 지정할 수 있게 하는 수정자들이 새로 등장했습니다 

여기서는 scrollTargetLayout 수정자를 사용해 Lazy Stack에 있는 히어로 뷰를 각각 스크롤 타깃으로 삼습니다 

스크롤 타깃 수정자를 사용해서 각각의 뷰를 타깃으로 지정할 수도 있습니다 

 

Lazy Stack을 사용할 때는 scrollTargetLayout 수정자를 사용하는 것이 중요합니다

가시 영역 바깥의 뷰는 아직 만들어지지 않았지만 레이아웃은 자신이 어떤 뷰를 만들게 될지 압니다.

그래야 ScrollView가 옳은 위치로 스크롤하게 할 수 있으니까요

 

 

ScrollTargetBehavior 프로토콜 커스텀

여러분의 타입이 ScrollTargetBehavior를 준수하게 하려면 유일하게 필수인 메서드인 updateTarget을 구현해야 합니다

SwiftUI는 스크롤을 어디서 끝낼지 계산할 때 이 메서드를 호출하지만 ScrollView 크기가 바뀔 때 등 다른 경우에도 호출합니다

이 동작을 사용자화하는 건 간단합니다

타깃이 ScrollView 위쪽에 가깝고 스크롤이 위로 휙 넘어갈 때 주어진 타깃을 변경하여 ScrollView의 꼭대기까지 스크롤이 올라가게 하고 싶습니다

이렇게 하면 ScrollView는 다른 콘텐츠 오프셋을 선택해 스크롤의 끝점으로 삼게 됩니다

ScrollView로 스크롤할 범위에 영향을 미치려면 맞춤형 코드를 삽입해 이렇게 하기만 하면 됩니다

 

[containerRelativeFrame]

뷰의 크기는 기기 전체의 너비에 비례합니다 

그리고 iPad에서는 뷰 두 개가 기기 너비에 고르게 맞춰져 있음

예전에는 이렇게 하려면 GeometryReader를 써야 했지만 

containerRelativeFrame 수정자란 API가 새로 생겼기 때문에 훨씬 쉬워졌습니다

 

고정 높이를 지정하는 프레임 수정자를 만듭니다 

그다음 containerRelativeFrame 수정자를 뷰에 추가하겠습니다 

여기서 축을 가로로 지정해서 뷰의 너비가 컨테이너 너비에 맞춰지게 합니다 

 

여기선 주위를 둘러싼 ScrollView가 컨테이너지만 내비게이션 Split View에서 가장 가까운 열이나 앱의 창도 컨테이너가 될 수 있죠 컨테이너의 너비가 바뀌면 제 뷰의 크기도 자동으로 업데이트됩니다

 

count와 spacing을 지정하면 뷰의 레이아웃을 그리드 모양으로 만들 수 있습니다

 

가로 sizeClass에 따라 count를 조건부로 지정해서 iPad에서는 2열로 iPhone에서는 1열로 할 수도 있음

aspectRatio 수정자를 사용해 높이를 너비에 비례하도록 조정가능 합니다

 

[scrollIndicators]

스크롤 인디케이터가 눈에 띄는데 이걸 없애고 싶다면 기존의 scrollIndicators API로 없앨 수는 있습니다

하지만 인디케이터를 감춰 달라고 요청했는데도 마우스를 연결하면 인디케이터가 드러남 

스크롤 인디케이터가 없으면 마우스 스크롤이 어렵거나 불가능할 수 있음

그래서 scrollIndicator 수정자의 기본값은 트랙패드처럼 유연한 입력 기기를 사용할 때는 인디케이터를 숨기고 마우스를 연결했을 때는 인디케이터를 보이는 겁니다

never로 지정해서 입력 장치와 무관하게 인디케이터를 숨겨도 됨

 

[scrollPosition]

SwiftUI 이전 버전이었다면 ScrollViewReader를 써서 알맞은 뷰로 스크롤하게 했겠지만

최신 SwiftUI에는 scrollPosition 수정자가 있음

뷰 안에서 다른 상태값을 선언하듯이 바인딩에 쓰기를 할 수 있습니다

바인딩에 쓰기를 마치면 ScrollView는 그 ID에 해당하는 뷰로 스크롤하게 됩니다

 

Scroll transitions 

어떤 뷰가 스크롤되고 있는지를 아는 게 유용한 것처럼 때로 전 ScrollView 안에서 뷰가 어디 있느냐에 따라 뷰의 모습을 시각적으로 바꾸고 싶은데 SwiftUI에 새로 생긴 ScrollTransition API를 사용하면 손쉽게 바꿀 수 있습니다

 

ScrollTransition은 일반적인 전환과 비슷합니다 

전환은 뷰가 나타나거나 사라질 때 뷰에 일어나야 하는 변화를 묘사함

뷰가 나타날 때 뷰는 identity phase에 있기에 어떤 사용자 설정도 적용해서는 안 됩니다

ScrollTransition은 전환과 비슷한 일련의 변화를 묘사하지만 뷰가 ScrollView의 가시 영역에 들어오는 순간과 나가는 순간에 이러한 변화를 적용합니다

 

뷰가 ScrollView의 가장자리에 가까워질수록 뷰의 크기를 살짝 줄이고 싶습니다 

먼저 scrollTransition 수정자를 추가하겠습니다

이 API에는 content와 phase가 들어가며 phase를 근거로 콘텐츠에 시각적 변화를 지정할 수 있습니다

뷰가 identity phase에 있지 않을 때 크기를 줄이겠다고 지정하겠습니다

 

ScrollTransition은 새 프로토콜인 VisualEffect와 함께 작동합니다

이 프로토콜로 뷰 콘텐츠에 할 수 있는 사용자 설정은 레이아웃의 함수로서 안전하게 사용할 수 있습니다.

이미알고있는 scaleEffect, rotationEffect, offset도 사용자화할 수 있습니다

뷰 수정자로 할 때와 비슷합니다

하지만 모든 뷰 수정자를 scrollTransition 내에서 안전하게 쓸 수 있는 건 아닙니다

예를 들어 서체 사용자 설정은 지원되지 않고 빌드되지 않습니다

ScrollView 콘텐츠 전체의 크기를 바꾸는 것들은 scrollTransition 수정자 안에서 쓸 수 없음

 

 

아래의 새로운 API에 대해서 지금까지 설명했습니다.

 

반응형