.getSize()

SwiftUIViewsView ⟩ .getSize()

⭐️ 注意

雖然可用此方法來更新 State 變數,但要小心不要造成 view states 不斷地變化,否則可能會形成 .getSize → update states → update view body → .getSize 的無限迴圈❗️❗️❗️

⭐️ 注意

如果違反 "source of truth" 原則,就算用了 DispatchQueue 的招數也是沒用❗️ 👉 StackOverflow

// 2022.02.18
// 2022.02.19 (f) + DispatchQueue.main.async
extension View {
    /// get view's size and do something with it.
    /// ```
    /// view.getSize { size in ... }
    /// ```
    func getSize(action: @escaping (CGSize) -> Void) -> some View {
        background(GeometryReader{ geo in
            emptyView(size: geo.size, action: action)
        })
    }
    
    // private empty view
    private func emptyView(
        size  : CGSize, 
        action: @escaping (CGSize) -> Void
    ) -> some View {
        
        // ----------------------------------------------------------
        // 🆘 問題:
        //    如果 action() 裡面有 update view state 的話,
        //    會產生 rendering cycle:
        //    • refresh -> .getSize() -> update view state -> refresh
        //    SwiftUI redering engine 會拒絕執行這些動作❗️❗️❗️
        // ----------------------------------------------------------
        // 💊 解藥:
        //    如果要避免這樣情況發生,目前的做法是將 action()
        //    放到 DispatchQueue 裡面。
        // ----------------------------------------------------------
        DispatchQueue.main.async {
            action(size)            // ⭐️ side effect❗️
        }
        
        return EmptyView()
    }
}

History

  1. 2022.02.19

Last updated