-
SwiftUI 스크롤 뷰의 임계값 삽질하기SwiftUI 2025. 1. 16. 19:00
안녕하세요. 그린입니다 🍏
이번 포스팅에서는 어떠한 학습 정보 전달보다는 그냥 삽질을 하는 과정과 그걸 기록해보는 포스팅입니다 🙋🏻
SwiftUI의 스크롤 뷰의 임계값 삽질하기
우선, 아래 코드를 봐볼까요?
import SwiftUI struct ContentView: View { var body: some View { ScrollView { VStack(spacing: 50) { Rectangle() .fill(.red) .frame(height: 100) Rectangle() .fill(.green) .frame(height: 100) Rectangle() .fill(.blue) .frame(height: 100) } .padding(.horizontal, 20) } } }
정말 별거 아닌 단순한 스크롤 뷰입니다.
여기서 스크롤 시 컨텐츠는 어디까지 보여질까요?
safeArea 영역에서도 보여질까요?
스크롤 시 safeArea 영역으로도 컨텐츠가 노출되는걸 볼 수 있어요.
기본적으로 SwiftUI의 스크롤뷰는 safeArea 밖으로도 스크롤이 가능하도록 설계가 되어 있습니다 😃
그렇기에 따로 padding을 주지 않는다면 스크롤 할 때 컨텐츠가 safeArea을 넘어서도 보이는거죠.
근데, 나는 safeArea를 침범하고 싶지 않은데?
이걸로 한번 임계값 삽질기가 시작되었어요.
padding을 줘볼까?
import SwiftUI struct ContentView: View { var body: some View { ScrollView { VStack(spacing: 50) { Rectangle() .fill(.red) .frame(height: 100) Rectangle() .fill(.green) .frame(height: 100) Rectangle() .fill(.blue) .frame(height: 100) } .padding(.horizontal, 20) } .padding(.top, 🤔) } }
스크롤뷰에 top padding으로 값을 준다면 해결됩니다.
아래와 같이 safeArea를 넘어서 보이지 않게되죠.
다만 여기서 padding을 사용할때 가장 큰 문제가 있습니다.
스크롤 뷰에 padding을 준다는건 컨텐츠의 시작 y 포지션이 달라질 수 있는거죠.
즉, 공백을 가지고 시작하는겁니다.
어? 나는 공백을 주기 싫은데..!
그래서 padding 값으로 0을 줘봤어요.
그랬더니, safeArea을 넘어서 보이게되어 의미가 없어졌습니다.
그럼 여기에 넣었을때 safeArea도 넘지 않는 가장 작은 임계값은 얼마일까?
노가다를 해봤습니다.
그랬을때 0.1677....이 나오더라구요 🫣
뭐지 이 애매한 값은?해당 수치 이상으로 값을 넣으면 스크롤 뷰가 safeArea를 인식하고 바운스를 제한하게 됩니다.
그럼 왜 0.1677...의 임계값을 가질까?
그건 애플이 공식적으로 문서화하지 않았기에 알 수 없습니다 🥲
아마도 추측하건데 SwiftUI의 내부 렌더링 시스템에서 사용하는 임계값(threshold)이지 않을까 싶어요.
1️⃣ 해당 임계값을 기준으로 레이아웃 시스템이 다르게 동작하도록 설계되었을것 같다.
2️⃣ 디바이스의 물리적 픽셀과 포인트 관계에서 발생하는 최소 단위와 관련이 있을것 같다.
3️⃣ 성능 최적화를 위해 특정 임계값 이하의 패딩은 무시되도록 설계되었을 수도 있을것 같다.
이런 추측들을 해봤습니다.
물론, 이 추측들을 확실히 검증하거나 그런 레퍼런스를 찾진 못했지만요 🥲
그럼 이 임계값을 사용하는게 최선일까?
아닙니다.
임계값을 아무리 작게 가져가서 티가 안나게 우회하여 구현할 수 있더라도 이런 padding 임계값을 사용하는건 좋지 않은 방법이라고 생각됩니다.
그 이유는 임계값에 의존하면 위험한것에 있는데, 애플이 향후에 SwiftUI를 업데이트하면서 이 동작과 임계값의 수치를 언제든지 변경할 수도 있어요.
내부적으로 말이죠.
그렇게 된다면 우리는 문서화로 나와있지 않은 정보이기에 알 수 없고 매번 조정을 해야될 수도 있어요.
또한, 아예 동작이 바뀌면 의미가 없게 됩니다.
즉, 해당 코드는 언제든 버려질 수 있는 코드가 되는것이죠.
그럼 어떻게 해야 임계값없이 스크롤 뷰의 safeArea를 넘지 않는 동작을 만들어볼 수 있을까?
물론, UIScrollView를 사용해서 래핑해도 되겠지만 SwiftUI에서 더 최선의 방법을 생각해봤습니다.
코드로 볼까요?
import SwiftUI struct ContentView: View { var body: some View { ScrollView { VStack(spacing: 50) { Rectangle() .fill(.red) .frame(height: 100) Rectangle() .fill(.green) .frame(height: 100) Rectangle() .fill(.blue) .frame(height: 100) } .padding(.horizontal, 20) } .modifier(ScrollClipModifier()) } } struct ScrollClipModifier: ViewModifier { func body(content: Content) -> some View { content .clipShape(Rectangle()) } }
저는 여기서 ScrollClipModifier를 만들었어요.
해당 모디파이어 구현을 보시면 clipShape를 사용하고 있습니다.
즉, 해당 모디파이어는 뷰의 컨텐츠를 지정된 Shape인 Rectangle 범위로 잘라내줍니다.
스크롤 뷰가 스크롤 될 때 safeArea를 벗어나는 컨텐츠를 자동으로 숨겨주는거죠.
Rectangle로 한 이유는 직사각형 형태로 깔끔하게 클립되게 하여 자연스러움을 주기 위함이에요.
이 모디파이어를 스크롤 뷰에 사용하면 끝이죠.
구현이 매우 간단하고 성능적인 오버헤드가 거의 없습니다.
특히, SwiftUI의 선언적 특성 자체를 잘 살려주고 있고 모든 iOS 버전에 일관된 동작을 보장해줍니다.
그래서 이렇게 간단한 구현으로 safeArea를 넘지 않는 스크롤 뷰 동작을 제공해볼 수 있습니다 😃
마무리
SwiftUI의 스크롤 뷰 padding 임계값 관련하여 간단하지만 알아가보는 과정은 아주 재밌었어요 😁
그렇지만 계속 말했듯 프로덕션 코드에서는 이를 사용하면 좋지 않다고 생각됩니다.
왜냐면 예측할 수 없기때문이죠.
그렇기에 우리는 항상 예측 가능한 사용자 경험을 제공하기 위해 더 안정적인 해결 방법들을 찾아 나가야 합니다 🏃🏻
'SwiftUI' 카테고리의 다른 글
SwiftUI - ScrollBounceBehavior (2) 2025.01.20 SwiftUI에서 UUID를 활용한 뷰 갱신 업데이트 (2) 2025.01.09 SwiftUI Text에 stroke 적용하기 (feat. UIKit) (21) 2025.01.02 SwiftUI의 Custom Grid로 카테고리 뷰 구현하기 (33) 2024.12.23 UIGestureRecognizerRepresentable 사용하기 (34) 2024.12.16