SwiftUI GeometryReader, ebeveyninin durumunu güncelleyemedi

Oct 12 2020

Dün şu gönderiye rastladım: SwiftUI'de GeometryReader nedir? ve davranışını merak ediyordum GeometryReader, bu yüzden bazı deneyler yaptım ve aşağıdaki yazıyı okuduktan sonra yaptığım şey:

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())

Sonuç:

Yukarıdan, iki yaklaşım kullandım ContentViewve @Statedeğişkenlerini güncelleyerek güncellemeye çalıştım , ikinci yaklaşım başarılı olsa da, ilki başarısız oldu, neden başarısız olduğunu bilen var mı? Teşekkürler.

Yanıtlar

1 Asperi Oct 12 2020 at 11:12
   //    but SwiftUI ignores this assignment, why?

Çünkü işleme döngüsü üretecektir (değişen durum yenilemeyi başlatır, bu durum değişir vb.) Ve SwiftUI oluşturma motoru bu tür güncellemeleri bırakacak kadar akıllıdır.

Böyle bir güncellemeyi sonraki olay döngüsü için ertelemeniz gerekir, örneğin

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
}

Örneğin, bu yaklaşıma bakın. içindehttps://stackoverflow.com/a/60214735/12299030