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
}