.reportWidth() ...
主要功能
- view.reportWidth(to: key) 回報自己的寬度 (geometry.size.width) 給 - key(PreferenceKey)。
- view.reportFrame(to: key, in: space) 回報自己的 geometry.frame(in: space) 給 - key。
import SwiftUI
// 🌀View + pref (generic methods)
extension View {
    
    // ⭐️ 回報 GeometryProxy 的屬性給 `key`
    //    - view.report(to: key) { $0.size }
    public func report<K: PreferenceKey>(
        
        // 要回報的對象 (PreferenceKey)
        to key: K.Type = K.self,
        
        // 要回報 GeometryProxy 的哪個屬性?
        // ⚠️ 注意:
        //    這裡的 `value` closure 必須標註為 `@escaping`,
        //    因為它用在 GeometryReader 的 @ViewBuilder closure 裡面,
        //    而這個 closure 本身就是 `@escaping`,所以如果沒有標註的話,
        //    會產生 "escaping closure captures non-escaping parameter" 的錯誤 ‼️
        value : @escaping (GeometryProxy) -> K.Value
        
    ) -> some View {
        self
            // ⭐️ 1. read self's geometry
            .background(GeometryReader { geo in  
                Color.clear
                    // ⭐️ 2. set preference:
                    //       report geo's attribute to `key`
                    .preference(key: key, value: value(geo))
            })
    }
    
    // view.register(to: key) { $0.frame(in: space) } 
    // T:收集的個別資料型別
    // K:PreferenceKey, K.Value == [T]
    public func register<T, K: PreferenceKey>(
        to key: K.Type = K.self, 
        value : @escaping (GeometryProxy) -> K.Value.Element
    ) -> some View 
        where K.Value == Array<T>  // ⭐️ 收集到 K.Value == [T]
    {
        self
            // ⭐️ 1. read self's geometry
            .background(GeometryReader { geo in  
                Color.clear
                    // ⭐️ 2. set preference:
                    //       (register geo's value to `key`)
                    .preference(key: key, value: [value(geo)])
            })
    }
    
}
// specialized methods
extension View {
    
    // report width
    // view.reportWidth(to: key)
    public func reportWidth<K: PreferenceKey>(
        to key: K.Type = K.self
    ) -> some View 
        where K.Value == CGFloat? 
    { 
        self.report(to: key) { $0.size.width } 
    }
    
    // report frame
    // view.reportFrame(to: key, in: space)
    public func reportFrame<K: PreferenceKey>(
        to key: K.Type = K.self, 
        in  space: CoordinateSpace = .global
    ) -> some View 
        where K.Value == CGRect? 
    { 
        self.report(to: key) { $0.frame(in: space) } 
    }
    
    // register frame
    // view.registerFrame(to: key, in space)
    public func registerFrame<K: PreferenceKey>(
        to key: K.Type = K.self, 
        in  space: CoordinateSpace = .global
    ) -> some View 
        where K.Value == [CGRect]
    { 
        self.register(to: key) { $0.frame(in: space) } 
    }
    
}import SwiftUI
// 🌀View + anchor preference (generic methods)
extension View {
    
    // view.register(to: FrameAnchors.self)
    public func register<K: PreferenceKey>(to key: K.Type = K.self) -> some View 
        where K.Value == [Anchor<CGRect>]
    {
        self.anchorPreference(key: K.self, value: .bounds) { [$0] }
    }
    
    // view.overlay(with: FrameAnchors.self) { anchors in ... }
    public func overlay<K: PreferenceKey, T: View>(
        with  key: K.Type = K.self, 
        transform: @escaping (K.Value) -> T
    ) -> some View 
        where K.Value == [Anchor<CGRect>]
    {
        overlayPreferenceValue(key) { transform($0) } 
    }
    
}Last updated
Was this helpful?