Что такое PreferenceKey в SwiftUI?

«Что такое PreferenceKey в SwiftUI?» — вопрос из категории SwiftUI, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

PreferenceKey — это протокол в SwiftUI, который позволяет дочерним представлениям передавать данные вверх по иерархии к общему предку, не требуя прямых связей. Это ключевой механизм для координации состояния между представлениями.

Основные компоненты:

  1. defaultValue: Статическое значение по умолчанию.
  2. reduce(value:nextValue:): Функция, которая объединяет значения от всех дочерних представлений в одно.

Зачем это нужно? Для реализации сложной компоновки, когда родительскому виду нужно знать размеры или состояние своих детей (например, для выравнивания, построения тулбаров или кастомной навигации).

Пример: Передача размера дочернего вида

// 1. Определяем ключ предпочтения
struct SizePreferenceKey: PreferenceKey {
    static var defaultValue: CGSize = .zero
    static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
        value = nextValue() // В данном примере берем последнее значение
    }
}

// 2. Дочернее представление отправляет свой размер
struct ChildView: View {
    var body: some View {
        Color.blue
            .frame(width: 200, height: 150)
            .background(GeometryReader { geometry in
                Color.clear
                    .preference(key: SizePreferenceKey.self,
                                value: geometry.size)
            })
    }
}

// 3. Родительское представление читает и использует размер
struct ParentView: View {
    @State private var childSize: CGSize = .zero

    var body: some View {
        VStack {
            ChildView()
            Text("Размер ребенка: (Int(childSize.width))x(Int(childSize.height))")
        }
        .onPreferenceChange(SizePreferenceKey.self) { newSize in
            childSize = newSize
        }
    }
}

Ключевые моменты:

  • Для чтения геометрии часто используется GeometryReader.
  • onPreferenceChange срабатывает каждый раз, когда значение ключа меняется в любом дочернем представлении.
  • Метод reduce определяет, как агрегировать множественные значения (например, можно суммировать их или находить максимум).