Size of View in SwiftUI

Viewed 469

Here I am reading my View Size in a background process, everything works fine except than sending the un-wished value!

In this down code, I used (CGSize) -> Content, and It send the exist value of CGSize, which means CGSize() or (0.0, 0.0). Which it make sense because it is first finable value, but as you see in the codes I am calculating the needed value and I want send that value.

my Goal: how can I send my (CGSize) -> Content which CGSize is calculated.

PS: I am thinking using completionhandler on CGSize.

Something like this but I am not sure:

var content: (@escaping () -> CGSize) -> Content

or even this:

var content: (@escaping (Content) -> CGSize) -> Content

In this way that it waits until CGSize get calculated then sent it with Content together, as you can see in (CGSize) -> Content it capture first possible CGSize!


Console:

(94.66666666666666, 20.333333333333332)
read size onAppear is: (0.0, 0.0)
read size onChange is: (94.66666666666666, 20.333333333333332)

import SwiftUI

struct ContentView: View {
    
    var body: some View {
        
        CustomView { size in
            
            Text("Hello, world!")
                .background(Color.yellow)
                .onAppear() {
                    print("read size onAppear is:", size.debugDescription)
                }
                .onChange(of: size) { newValue in
                    print("read size onChange is:", newValue.debugDescription)
                }

        }
        
    }
}

struct CustomView<Content: View>: View {
    
    @State private var sizeOfText: CGSize = CGSize()
    
    var content: (CGSize) -> Content
    
    var body: some View {
        
        return content(sizeOfText)
            .background(
                GeometryReader { geometry in
                    Color.clear
                        .onAppear() { sizeOfText = geometry.size; print(sizeOfText) }
   
                })
        
    }
}
1 Answers

I could be able to find the magical answer:

extension View {
    
    func sizeReader(size: @escaping (CGSize) -> Void) -> some View {
        
        return self
            .background(
                GeometryReader { geometry in
                    
                    Color.clear
                        .preference(key: ContentSizeReaderPreferenceKey.self, value: geometry.size)
                        .onPreferenceChange(ContentSizeReaderPreferenceKey.self) { newValue in size(newValue) }
                    
                }
                .hidden()
            )
        
    }
    
}

struct ContentSizeReaderPreferenceKey: PreferenceKey {
    
    static var defaultValue: CGSize { get { return CGSize() } }
    
    static func reduce(value: inout CGSize, nextValue: () -> CGSize) { value = nextValue() }
    
}

Use case:

You can use .sizeReader on any View you like to read or save the Size of View. It is reverse GeometryReader, and I can say it make a significant difference in your app or project when you need to read or work with the size of a View! Look how much would be cleaner and easier using .sizeReader in your project.

struct ContentView: View {
    
    var body: some View {
        
        Text("Hello, world!")
            .sizeReader { size in print(size) }
        
    }
}
Related