Sobes.tech
Middle+

What is PreferenceKey in the context of SwiftUI?

sobes.tech AI

Answer from AI

PreferenceKey is a protocol in SwiftUI that allows passing values "up" the view tree. It is used to gather information from child views and deliver it to parent views, which can then use this information to modify their behavior or appearance.

Key features of PreferenceKey:

  • Protocol: Implement the PreferenceKey protocol, defining the value type and a method for combining values.
  • associatedtype Value: Defines the data type to be passed.
  • static func reduce(value: inout Value, nextValue: () -> Value): Called by SwiftUI to combine values from multiple child views. You define how to merge the current value (value) with the new one (nextValue()).
  • Passing values: Child views use the .preference(key:value:) modifier to set a value for a specific PreferenceKey.
  • Reading values: Parent views use .onPreferenceChange(_:perform:) to get notified of changes to the value for a specific PreferenceKey.

Example:

Creating a PreferenceKey to determine the height of a child view:

struct ChildHeightPreferenceKey: PreferenceKey {
    typealias Value = Double?
    static var defaultValue: Double? = nil
    static func reduce(value: inout Double?, nextValue: () -> Double?) {
        value = nextValue() ?? value
    }
}

Using in a view:

struct ParentView: View {
    @State private var childHeight: Double? = nil
    var body: some View {
        VStack {
            Text("Child height: \(childHeight != nil ? "\(childHeight!)" : "-")")
            ChildView()
                .onPreferenceChange(ChildHeightPreferenceKey.self) { newValue in
                    self.childHeight = newValue
                }
        }
    }
}

struct ChildView: View {
    var body: some View {
        GeometryReader { geometry in
            Rectangle()
                .fill(Color.blue)
                .frame(height: 100)
                .preference(key: ChildHeightPreferenceKey.self, value: geometry.size.height)
        }
        .frame(height: 100)
    }
}