PreferenceKey

2025. 10. 24. 22:59·iOS/SwiftUI
반응형

👋 들어가기 전

SwiftUI를 통해 클론코딩을 할 때, 많이 등장하는 개념인데, 이제는 슬슬 학습할 때가 된 것 같다.

 

현재하고 있는 플젝에서도 적극적으로 도입할 예정!


🔑 PreferenceKey

 

🧩 역할

A view with multiple children automatically combines its values for a given preference into a single value
visible to its ancestors.

 

하위뷰가 상위뷰로 전달하고 싶은 값(Preference)을 지정할 떄 사용하는 Key

 

보통 상위 -> 하위로는 많이 전달했지만, 하위 -> 상위로 전달하는 역전 형태는 PreferenceKey를 이용하면
깔끔하게 처리할 수 잇음

 

🎯 자주쓰이는 용도

  • 뷰 크기/위치 측정
  • 스크롤 위치 추적
  • 커스텀 레이아웃

 

🧱 구조

struct MyPreferenceKey: PreferenceKey {

    associatedtype Value
    static var defaultValue: Self.Value
    static func reduce(value: inout Self.Value, nextValue: () -> Self.Value)

}

 

  • defaultValue
    • 명시적인 초기 값이 없을 경우, 사용할 기본 값
  • reduce
    • 이전에 누적된값을 수정하여, 하나의 값으로 결합 하는 함수
    • view-tree 순서로 값을 받음 (아래에서 설명)
    • value
      • 이전 호출의 결과로 누적된 값
    • nextValue
      • 새로 전달 받은 값

 

📨 값 수신 받기

onPreferenceChange(_:perform:) 메서드를 이용

 

 @preconcurrency @inlinable nonisolated public func onPreferenceChange<K>(
     _ key: K.Type = K.self, 
    perform action: @escaping @Sendable (K.Value) -> Void
) -> some View where K : PreferenceKey, K.Value : Equatable

 

수신 받을 PreferenceKey를 넣어주고, closure로 갱신된 값을 받는다.

 

📤 값 송신 하기

preference(key:value:) 메서드 이용

nonisolated
func preference<K>(
    key: K.Type = K.self,
    value: K.Value
) -> some View where K : PreferenceKey

 

Key에 해당하는 value를 넣어줌, 약간 딕셔너리 같음

 

🤖 예제 코드

예제 코드를 통해, 위에서 설명한 view-tree 순으로 호출된다는 느낌을 알아보자.

 

1️⃣ Key 선언

문자열 배열을 계속 누적함, 먼저 호출들어오는 값이 누군지 추적하려고 만듬

// 1) PreferenceKey 정의 — 값은 문자열 배열
struct OrderPreferenceKey: PreferenceKey {
    static var defaultValue: [String] = []

    // reduce가 어떻게 호출되는지 콘솔로 확인할 수 있게 print 추가
    static func reduce(value: inout [String], nextValue: () -> [String]) {
        let next = nextValue()
        print("reduce called. accumulated: \(value) next: \(next)")
        // "view-tree order"를 그대로 보존하려면 append 순서 유지
        value.append(contentsOf: next)
    }
}

 

2️⃣ 하위 뷰, 송신자 선언

struct ChildLabel: View {
    let id: String
    var body: some View {
        Text("Child \(id)")
            .padding(8)
            .background(Color(white: 0.95))
            // preference로 자신의 식별자 전달
            .preference(key: OrderPreferenceKey.self, value: [id])
            .onAppear {
                print("onAppear child \(id)")
            }
    }
}

 

3️⃣ 부모뷰, 수신자 선언

struct PreferenceOrderDemoView: View {
    @State private var receivedOrder: [String] = []

    var body: some View {
        VStack(spacing: 20) {
            Text("Received order: \(receivedOrder.joined(separator: ", "))")
                .padding()
                .multilineTextAlignment(.center)

            // 구조 1: 평평한(Flat) 선언 순서 A, B, C
            VStack {
                Text("Flat order (A, B, C):")
                HStack {
                    ChildLabel(id: "A")
                    ChildLabel(id: "B")
                    ChildLabel(id: "C")
                }
            }
            .padding()

            // 구조 2: 중첩 구조 (D 안에 E, F) 그리고 G — depth-first 예상: E, F, D, G 혹은 D 먼저?
            VStack {
                Text("Nested order (D contains E & F, then G):")
                VStack {
                    HStack {
                        ChildLabel(id: "E")
                        ChildLabel(id: "F")
                    }
                    // D를 표현하기 위해 E/F와 함께 D도 preference를 넣어봄
                    ChildLabel(id: "D")
                }
                ChildLabel(id: "G")
            }
            .padding()
        }
        .onPreferenceChange(OrderPreferenceKey.self) { value in
            print("onPreferenceChange received: \(value)")
            receivedOrder = value
        }
        .padding()
    }
}

 

4️⃣ 구조 및 결과해석

 

구조는 위에서 아래로, 왼쪽에서 오른쪽 순이 먼저다.

트리의 in-order 방식으로 보면 될 것 같다.

 

출력도 한번 비교해보자.

reduce called. accumulated: ["A"] next: ["B"]
reduce called. accumulated: ["A", "B"] next: ["C"]
reduce called. accumulated: ["E"] next: ["F"]
reduce called. accumulated: ["E", "F"] next: ["D"]
reduce called. accumulated: ["E", "F", "D"] next: ["G"]
reduce called. accumulated: ["A", "B", "C"] next: ["E", "F", "D", "G"]
onPreferenceChange received: ["A", "B", "C", "E", "F", "D", "G"]
onAppear child G
onAppear child D
onAppear child F
onAppear child E
onAppear child C
onAppear child B
onAppear child A

출처

https://developer.apple.com/documentation/swiftui/preferencekey

 

PreferenceKey | Apple Developer Documentation

A named value produced by a view.

developer.apple.com

 

반응형

'iOS > SwiftUI' 카테고리의 다른 글

커스텀 DynamicScrollTabVIew 만들기  (0) 2025.10.26
@NameSpace  (0) 2025.10.25
.id  (0) 2025.10.23
containerRelativeFrame  (0) 2025.10.23
SwiftUI 동작 방식  (0) 2025.10.18
'iOS/SwiftUI' 카테고리의 다른 글
  • 커스텀 DynamicScrollTabVIew 만들기
  • @NameSpace
  • .id
  • containerRelativeFrame
Hamp
Hamp
남들에게 보여주기 부끄러운 잡다한 글을 적어 나가는 자칭 기술 블로그입니다.
  • Hamp
    Hamp의 분리수거함
    Hamp
  • 전체
    오늘
    어제
    • 분류 전체보기 (304)
      • CS (30)
        • 객체지향 (2)
        • Network (7)
        • OS (6)
        • 자료구조 (1)
        • LiveStreaming (3)
        • 이미지 (1)
        • 잡다한 질문 정리 (0)
        • Hardware (2)
        • 이론 (6)
        • 컴퓨터 그래픽스 (0)
      • Firebase (3)
      • Programing Langauge (37)
        • swift (32)
        • python (4)
        • Kotlin (1)
      • iOS (132)
        • UIKit (37)
        • Combine (1)
        • SwiftUI (32)
        • Framework (7)
        • Swift Concurrency (22)
        • Tuist (6)
        • Setting (11)
        • Modularization (1)
        • Instruments (6)
      • PS (59)
        • 프로그래머스 (24)
        • 백준 (13)
        • LeetCode (19)
        • 알고리즘 (3)
      • Git (18)
        • 명령어 (4)
        • 이론 (2)
        • hooks (1)
        • config (2)
        • action (7)
      • Shell Script (2)
      • Linux (6)
        • 명령어 (5)
      • Spring (13)
        • 어노테이션 (1)
        • 튜토리얼 (11)
      • CI-CD (4)
      • Android (0)
        • Jetpack Compose (0)
  • 블로그 메뉴

    • 홈
    • 태그
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    백준
    IOS
    dfs
    concurrency
    투포인터
    AVFoundation
    property
    GIT
    Spring
    UIKit
    lifecycle
    boostcamp
    dp
    dispatch
    CS
    Tuist
    Swift
    SwiftUI
    protocol
    프로그래머스
  • 최근 댓글

  • 최근 글

  • 반응형
  • hELLO· Designed By정상우.v4.10.0
Hamp
PreferenceKey
상단으로

티스토리툴바