Skip to content

PreferenceKey

최윤진 edited this page Mar 5, 2026 · 1 revision

SwiftUI의 PreferenceKey는 자식 뷰 → 부모 뷰로 값을 전달할 때 사용하는 메커니즘.
일반적인 @State, @Binding, Environment는 부모 → 자식 방향 데이터 흐름.
PreferenceKeychild → ancestor 방향으로 데이터를 전달하는 메커니즘.

즉, SwiftUI의 데이터 흐름에서 예외적으로 “위로 올라가는 데이터 전달”을 수행하는 도구. 🔁


1. 왜 PreferenceKey가 필요한가

SwiftUI의 기본 구조는 단방향 데이터 흐름.

Parent
  ↓
Child

하지만 특정 상황에서 Child의 정보를 Parent가 알아야 하는 경우의 존재.

예시:

  • 자식 뷰의 size
  • 자식 뷰의 position
  • 자식 뷰들의 custom value

구조 예시:

Parent
 └ ChildView

Parent가 필요로 할 수 있는 정보의 예시:

  • ChildView의 height
  • ChildView의 frame
  • 여러 child들의 값

이때 사용하는 도구가 PreferenceKey.


2. 기본 구조

PreferenceKey는 protocol.

protocol PreferenceKey {
    associatedtype Value

    static var defaultValue: Value { get }

    static func reduce(value: inout Value, nextValue: () -> Value)
}

각 요소의 의미:

요소 설명
Value 전달할 데이터 타입
defaultValue 기본값
reduce 여러 child가 있을 때 값을 합치는 방법

3. 간단한 예제

1️⃣ PreferenceKey 정의

struct TitlePreferenceKey: PreferenceKey {

    static var defaultValue: String = ""

    static func reduce(value: inout String, nextValue: () -> String) {
        value = nextValue()
    }
}

2️⃣ Child에서 값 설정

struct ChildView: View {
    var body: some View {
        Text("Hello")
            .preference(key: TitlePreferenceKey.self, value: "Hello Title")
    }
}

핵심 API: .preference(key:value:)

이 메서드의 역할은 값을 부모에게 전달하는 동작.

3️⃣ Parent에서 값 수신

struct ParentView: View {

    @State private var title: String = ""

    var body: some View {
        VStack {
            Text(title)

            ChildView()
        }
        .onPreferenceChange(TitlePreferenceKey.self) { value in
            title = value
        }
    }
}

데이터 ��름 구조:

ChildView
   │
.preference()
   │
   ▼
PreferenceKey
   │
   ▼
Parent .onPreferenceChange

4. reduce가 필요한 이유

Child가 여러 개 존재할 수 있기 때문.

Parent
 ├ Child1
 ├ Child2
 └ Child3

각 child가 값을 전달하는 구조.

  • Child1 → value1
  • Child2 → value2
  • Child3 → value3

reduce에서 여러 값을 어떻게 합칠지 결정하는 로직의 정의.

예시:

마지막 값 사용

value = nextValue()

배열로 수집

value.append(nextValue())

최대값 선택

value = max(value, nextValue())

5. 가장 많이 쓰는 실제 사례

SwiftUI에서 PreferenceKey의 대표적인 사용 사례는 child view size 측정 패턴.

PreferenceKey 정의

struct SizePreferenceKey: PreferenceKey {

    static var defaultValue: CGSize = .zero

    static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
        value = nextValue()
    }
}

Child 측 코드:

.background(
    GeometryReader { geo in
        Color.clear
            .preference(
                key: SizePreferenceKey.self,
                value: geo.size
            )
    }
)

Parent 측 코드:

.onPreferenceChange(SizePreferenceKey.self) { size in
    print(size)
}

이 구조는 SwiftUI에서 매우 널리 사용되는 패턴.


6. AnchorPreference (고급)

PreferenceKey의 확장 개념인 AnchorPreference의 존재.

뷰의 위치 정보를 anchor 형태로 전달하는 기능.

대표 사용 사례:

  • sticky header 구현
  • scroll tracking
  • view overlay positioning

관련 API: .anchorPreference(key:value:transform:)

SwiftUI의 다양한 layout 트릭에서 활용되는 핵심 기술.


7. 언제 쓰면 좋은가

다음 상황에서 PreferenceKey 사용의 적합성.

상황 해결 방법
child size 필요 PreferenceKey
child frame 필요 PreferenceKey
scroll offset 추적 PreferenceKey
여러 child 값 collect PreferenceKey

8. Environment vs PreferenceKey

특징 Environment PreferenceKey
방향 Parent → Child Child → Parent
목적 설정 전달 결과 전달
사용 theme, locale size, position

9. SwiftUI 내부 사용 사례

SwiftUI 내부 컴포넌트에서도 PreferenceKey 활용의 존재.

대표 사례:

  • NavigationTitle
  • Toolbar
  • List row background
  • ScrollView offset tracking

즉 SwiftUI layout 시스템의 핵심 기술 중 하나.


10. 한 문장 정리

PreferenceKey = SwiftUI에서 Child → Parent 방향으로 값을 전달하기 위한 공식적인 메커니즘

Clone this wiki locally