.getSize()
SwiftUI ⟩ Views ⟩ View ⟩ .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()
}
}
use GeometryReader.
revised from .readSize() to fix problem with .readSize().
History
2022.02.19
// 2022.02.18
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
// -----------------------------------------------------
// ⭐️ 注意:
// • 在 GeometryReader 裡面寫 `let _ = action(size)`
// 沒有任何作用❗️❗️❗️
// • 也不能直接寫 `action(size)`,因為裡面是個 ViewBuilder
// -----------------------------------------------------
// 💊 解藥:
// 只好另外寫一個 emptyView(),並且在它裡面「夾帶私貨」❗️
// -----------------------------------------------------
emptyView(size: geo.size, action: action)
})
}
// private empty view
private func emptyView(
size : CGSize,
action: @escaping (CGSize) -> Void
) -> some View {
action(size) // 💊 解藥:⭐️ 夾帶私貨❗️
return EmptyView()
}
}
Last updated