🅿️PreferenceKey
// ------------------------
// 🅿️ PreferenceKey
// ------------------------
public protocol PreferenceKey {
// value type
associatedtype Value
// default value
static var defaultValue: Self.Value { get }
// combine values from different children
static func reduce(
value : inout Self.Value,
nextValue: () -> Self.Value
)
}
// ------------------------
// View methods
// ------------------------
// 🔸 (child view) set view preference
func preference<K: PreferenceKey>(
key : K.Type = K.self,
value: K.Value
) -> some View
// 🔸 .onPreferenceChange()
// (parent view) react to view preference change
func onPreferenceChange<K>(
_ key: K.Type = K.self,
perform action: @escaping (K.Value) -> Void
) -> some View where
K : PreferenceKey,
K.Value : Equatable
// 🔸 .overlayPreferenceValue(key:transform:)
func overlayPreferenceValue<Key: PreferenceKey, T: View>(
_ key: Key.Type = Key.self,
_ transform: @escaping (Key.Value) -> T
) -> some View
// 🔸 .backgroundPreferenceValue(key:transform:)
func backgroundPreferenceValue<Key: PreferenceKey, T: View>(
_ key: Key.Type = Key.self,
_ transform: @escaping (Key.Value) -> T
) -> some Viewcan cause subtle problems, 👉 problem with .readSize().
Preferences
📦 PreferenceKeys
📦 Anchor
範例
// 📦 MaxValue<T: FloatingPoint>
public struct MaxValue<T: FloatingPoint>: PreferenceKey {
// value type
public typealias Value = T?
// default value (nil == not set)
public static var defaultValue: Value { nil }
// ⭐️ choose max value
public static func reduce(value: inout Value, nextValue: () -> Value) {
// ⭐️ [T?] --(compactMap)--> [T] --(max)--> T?
value = [value, nextValue()].compactMap{ $0 }.max()
}
}// 📦 AllValues<T>
public struct AllValues<T>: PreferenceKey {
// ⭐️ 收集的資料放在 [T] 裡面
public typealias Value = [T]
// ⭐️ 初始值:空陣列
public static var defaultValue: Value { Value() }
// ⭐️ 加入新資料的方法:[v1, v2, ...] + [vn]
public static func reduce(value: inout Value, nextValue: () -> Value) {
value += nextValue() // nextValue() == [vn]
}
}// 📦 FirstNonNil<T>
public struct FirstNonNil<T>: PreferenceKey {
public typealias Value = T?
// default value
public static var defaultValue: Value { nil }
// combine values from different child views
public static func reduce(value: inout Value, nextValue: () -> Value) {
value = value ?? nextValue() // nil or first non-nil value
}
}💈 對齊欄位 (
MaxWidth = MaxValue<CGFloat>)💈 MonthView (
Frames = AllValues<CGRect>)💈 CircleText (
MaxSide = FirstNonNil<CGFloat>)
📦 PreferenceKeys
Preferences
用法
設 K 為遵循 PreferenceKey 的真實型別 (concrete type),則使用這個 PreferenceKey 時,通常必須具備以下「三大要素」:
有了這些要素後,還要依照固定的模式,才能順利完成佈局,主要是以下的「三步驟」:
整個流程可以簡化為:
👉 比較:Anchor Preferences
⭐️ 取名原則
PreferenceKey 的名字最好是跟母視件的 @State 變數一致,例如: 如果我們要找的是某個子視件兩邊長中的最大邊,這時可以:
將母視件的 @State 變數取名為:
maxSide而 PreferenceKey 就取名為:
MaxSide
👉 參見:💈 CircleText
👉 自製型別:📦 PreferenceKeys
Last updated
Was this helpful?