반응형
SwiftUI Study – GeometryReader에 의존하지 않고 레이아웃을 안정적으로 구성하는 방법
1. 왜 중요한가 (문제 배경)
SwiftUI 초기에 GeometryReader는 “레이아웃을 자유롭게 만들 수 있는 도구”로 널리 소개되었지만,
실제 프로젝트에서 과도하게 사용하면 다음과 같은 문제가 발생합니다.
- 부모 뷰의 크기가 예상치 못하게 0 또는 무한대(∞)로 측정됨
- GeometryReader 내부 뷰가 예상과 다른 위치로 이동
- 스크롤/안전 영역(safe area)과 결합할 때 레이아웃이 깨짐
- 디바이스 회전, iPad 분할 화면에서 뷰 위치가 불안정해짐
문제의 핵심은 다음에 있습니다.
GeometryReader는 “자식이 부모의 크기를 결정하는 패턴”을 만들기 쉽다.
SwiftUI는 “부모가 자식의 크기를 결정하는 패턴”이 기본이다.
따라서 GeometryReader는 꼭 필요한 상황에서만 사용하고,
대부분의 경우는 다른 레이아웃 도구로 충분히 대체할 수 있습니다.
2. 잘못된 패턴 예시
❌ 예시 1: 단순 정렬/레이아웃을 위해 GeometryReader 남용
struct WrongView: View {
var body: some View {
GeometryReader { geo in
Text("Hello")
.position(x: geo.size.width / 2,
y: geo.size.height / 2) // ❌ 중앙 정렬을 GeometryReader로 강제
}
}
}
문제점
- 이 방식은 부모가 자식의 크기를 역으로 참조하는 구조
- NavigationBar / SafeArea 영향을 받으면 중앙이 아닌 위치로 이동
- rotation / iPad split에서 레이아웃이 쉽게 깨짐
❌ 예시 2: 배경 크기 계산하려고 GeometryReader 사용
struct WrongBackgroundView: View {
var body: some View {
GeometryReader { geo in
Color.blue
.frame(width: geo.size.width, height: geo.size.height) // ❌ 불필요한 패턴
}
}
}
문제점
- Color는 레이아웃을 꽉 채우는 기본 기능이 있음
- GeometryReader만큼 비용이 큰 view tree 확장이 불필요하게 발생
❌ 예시 3: ScrollView 안에서 GeometryReader 위치 계산
ScrollView {
GeometryReader { geo in
Text("상단 고정 텍스트")
.offset(y: geo.frame(in: .global).minY) // ❌ 스크롤 위치 기반 계산
}
}
문제점
- ScrollView는 지연 렌더링(lazy)이 아니라 내부 계산이 반복되어 성능 저하
- 뷰가 스크롤되는 순간 minY 값이 계속 바뀌며 레이아웃 흔들림 발생
- 흔히 “스크롤하면 텍스트가 날아다님” 같은 문제가 생김
3. 올바른 패턴 예시
✅ 예시 1: 중앙 정렬은 alignment와 frame으로 해결
struct CorrectCenterView: View {
var body: some View {
Text("Hello")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color.yellow)
.multilineTextAlignment(.center)
}
}
장점
- GeometryReader 없이도 안정적으로 중앙 정렬 가능
- 디바이스 회전, safe area, split-screen 상황에서도 깨지지 않음
✅ 예시 2: 배경은 safeAreaInset 또는 ZStack으로 해결
struct CorrectBackgroundView: View {
var body: some View {
ZStack {
Color.blue.ignoresSafeArea()
Text("Hello")
}
}
}
장점
- GeometryReader 없이 원하는 배경 연출 가능
- 선언적 SwiftUI 방식에 맞는 구조
✅ 예시 3: ScrollView 레이아웃에는 PreferenceKey 사용
struct OffsetPreferenceKey: PreferenceKey {
static var defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
}
struct CorrectScrollHeaderView: View {
@State private var offset: CGFloat = 0
var body: some View {
ScrollView {
Text("상단 고정 영역")
.offset(y: -offset)
.background(
GeometryReader { geo in
Color.clear
.preference(key: OffsetPreferenceKey.self,
value: geo.frame(in: .global).minY)
}
)
VStack(spacing: 20) {
ForEach(0..<30) { _ in
RoundedRectangle(cornerRadius: 10)
.frame(height: 80)
.foregroundColor(.gray.opacity(0.2))
}
}
}
.onPreferenceChange(OffsetPreferenceKey.self) { value in
offset = value
}
}
}
장점
- ScrollView 내부 레이아웃 계산이 안정됨
- GeometryReader를 직접 뷰 배치에 사용하지 않고, “측정만 수행”
- 흔들림/깜빡임 없이 부드러운 헤더 동작 구현 가능
4. 실전 적용 팁
✔ 팁 1 – GeometryReader는 “정보를 읽을 때만” 사용
뷰의 위치나 크기를 계산할 때만 사용하고,
레이아웃 배치(bounding / align / offset / position) 용도로 사용하지 않는 것이 안전함.
✔ 팁 2 – 레이아웃은 가능한 한 frame / alignment / ZStack으로 해결
GeometryReader를 쓰기 전에 다음을 먼저 생각해볼 것:
- alignment
- maxWidth / maxHeight
- safeAreaInset
- overlay / background
- ZStack + spacer
대부분 GeometryReader 없이 해결 가능함.
✔ 팁 3 – ScrollView 안의 GeometryReader는 성능에 가장 치명적임
지연 렌더링이 없는 GeometryReader는 스크롤할 때마다 크기 계산을 반복하여 성능 저하 발생.
✔ 팁 4 – Animation + GeometryReader 조합은 특히 취약
레이아웃 계산이 틀어지면 애니메이션이 튀거나 사라짐.
5. 정리
- GeometryReader 남용은 SwiftUI 레이아웃이 흔들리는 가장 큰 원인 중 하나이다.
- 레이아웃은 frame, alignment, safeAreaInset, overlay가 기본이고 GeometryReader는 보조 도구이다.
- GeometryReader는 “측정 전용”으로만 사용하면 레이아웃 문제를 크게 줄일 수 있다.
- ScrollView 내부의 GeometryReader는 매우 신중하게 사용해야 한다.
반응형
'Dev Study > SwiftUI' 카테고리의 다른 글
| SwiftUI Study – body 안에서 무거운 연산을 실행하지 않는 이유와 해결법 (0) | 2025.12.09 |
|---|---|
| SwiftUI Study – AnyView 남용을 피하고 타입 안정성을 유지하는 방법 (0) | 2025.12.09 |
| SwiftUI Study – View 중첩이 깊어지지 않도록 컴포넌트를 분리하는 실전 패턴 (0) | 2025.12.09 |
| SwiftUI Study – offset / position으로 레이아웃을 잡지 말아야 하는 이유 (0) | 2025.12.09 |
| SwiftUI Study – 상태 변경을 한 곳으로 모으는 패턴 (0) | 2025.12.09 |
| SwiftUI Study – FocusState를 안전하게 설계하는 방법 (0) | 2025.12.09 |
| SwiftUI Study – 도메인 상태와 UI 상태를 분리하는 방법 (0) | 2025.12.09 |
| SwiftUI Study – @State / @Binding / @ObservedObject / @StateObject / @EnvironmentObject 정확한 역할 이해하기 (0) | 2025.12.09 |

