SwiftUI GeometryReader gagal memperbarui status induknya
Kemarin saya menemukan posting ini: Apa itu GeometryReader di SwiftUI? , dan saya penasaran dengan perilakunya GeometryReader, jadi saya melanjutkan beberapa eksperimen, dan berikut ini yang saya lakukan setelah selesai membaca postingan:
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())
Hasil:
Dari atas, saya menggunakan dua pendekatan dan mencoba mengupdate ContentViewmelalui update @Statevariabel - variabelnya, walaupun pendekatan kedua berhasil, tetapi yang pertama gagal, adakah yang tahu mengapa gagal? Terima kasih.
Jawaban
// but SwiftUI ignores this assignment, why?
Karena itu akan menghasilkan siklus rendering (mengubah status memulai penyegaran, yang mengubah status, dan sebagainya), dan mesin rendering SwiftUI cukup pintar untuk menghentikan pembaruan tersebut.
Anda perlu menunda pembaruan tersebut untuk loop acara berikutnya, seperti
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
}
Lihat pendekatan ini penggunaan yang benar untuk mis. dihttps://stackoverflow.com/a/60214735/12299030