昨天我碰到了这篇帖子:What is GeometryReader in SwiftUI?,我对GeometryReader
的行为很好奇,因此我进行了一些实验,以下是我在阅读完该帖子后所做的事情:
import SwiftUI
import PlaygroundSupport
/* ------- Approach 1 ------- */
struct ViewGettingSize: View {
@Binding var size: CGSize
func makeView(with geometry: GeometryProxy) -> some View {
// ⭐️ Try to update @Binding var `size`,
// but SwiftUI ignores this assignment, why?
// @Binding var `size` is NOT updated.
self.size = geometry.size
print(geometry.size) // (158.5, 45.5)
print(self.size) // (50, 50)
return Color.pink
}
var body: some View {
GeometryReader { geo in
self.makeView(with: geo) // Color.pink
}
}
}
/* ------- Approach 2 ------- */
struct SizePreferenceKey: PreferenceKey {
static var defaultValue: CGSize = .zero
static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
value = nextValue()
}
}
struct ViewSettingSizePreference: View {
func makeView(with geometry: GeometryProxy) -> some View {
print(geometry.size) // (158.5, 45.5)
return Color.orange
.preference(key: SizePreferenceKey.self, value: geometry.size)
}
var body: some View {
GeometryReader { geo in
self.makeView(with: geo) // Color.orange
}
}
}
/* ------- Test These Approaches ------- */
let text = Text("some text").font(.largeTitle)
// live view
struct ContentView: View {
@State private var size = CGSize(50, 50)
@State private var size2 = CGSize(50, 50)
var body: some View {
VStack {
Group {
/* ------- Approach 1 ------- */
text
// ⭐️ this one doesn't work.
.background(ViewGettingSize(size: $size))
Color.blue
// ⭐️ `size` is still (50,50)
.frame(width: self.size.width, height: self.size.height)
/* ------- Approach 2 ------- */
text
// ⭐️ this one works.
.background(ViewSettingSizePreference())
.onPreferenceChange(SizePreferenceKey.self) { (size) in
print(size) // (158.5, 45.5)
self.size2 = size // ⭐️ `size2` updated successfully.
print(self.size2) // (158.5, 45.5)
}
Color.purple
.frame(width: self.size2.width, height: self.size2.height)
}// Group
.border(Color.black)
}// VStack (container)
.padding()
.background(Color.gray)
}
}
PlaygroundPage.current.setLiveView(ContentView())
结果:
从上面开始,我使用了两种方法,并尝试通过更新其ContentView
变量来更新@State
,尽管第二种方法成功了,但是第一种方法失败了,有人知道为什么会失败吗?谢谢。
答案 0 :(得分:1)
// but SwiftUI ignores this assignment, why?
因为它会产生渲染周期(更改状态会启动刷新,更改状态等等),而SwiftUI渲染引擎足够聪明,可以删除此类更新。
您需要为下一个事件循环延迟此类更新,例如
func makeView(with geometry: GeometryProxy) -> some View {
DispatchQueue.main.async {
self.size = geometry.size
}
print(geometry.size) // (158.5, 45.5)
print(self.size) // (50, 50)
return Color.pink
}
请参见此方法的正确用法,例如。在https://stackoverflow.com/a/60214735/12299030
中