SwiftUI GeometryReader未能更新其父项';s州
昨天我看到了这篇文章:,我对SwiftUI GeometryReader未能更新其父项';s州,swiftui,Swiftui,昨天我看到了这篇文章:,我对GeometryReader的行为很好奇,所以我进行了一些实验,下面是我读完这篇文章后所做的: import SwiftUI import PlaygroundSupport /* ------- Approach 1 ------- */ struct ViewGettingSize: View { @Binding var size: CGSize func makeView(with geometry: GeometryP
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())
结果:
从上面开始,我使用了两种方法,试图通过更新@State
变量来更新ContentView
,尽管第二种方法成功,但第一种方法失败了,有人知道它失败的原因吗?谢谢
因为它会产生渲染周期(更改状态会启动刷新,从而更改状态,等等),而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
}
查看此方法的正确用法,例如在中,但@Peter Warbo认为此方法有时会崩溃,不是吗?在我看来,这种方法对于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
}