.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) } 
    }
    
}

Last updated

Was this helpful?