Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Measure the rendered size of a SwiftUI view?

Tags:

swiftui

Is there a way to measure the computed size of a view after SwiftUI runs its view rendering phase? For example, given the following view:

struct Foo : View {
    var body: some View {
        Text("Hello World!")
            .font(.title)
            .foregroundColor(.white)
            .padding()
            .background(Color.red)
    }
}

With the view selected, the computed size is displayed In the preview canvas in the bottom left corner. Does anyone know of a way to get access to that size in code?

enter image description here

like image 551
Sean Rucker Avatar asked Jun 14 '19 18:06

Sean Rucker


People also ask

How do I get the size of a view in SwiftUI?

To make a SwiftUI view take all available width, we use . frame() modifier with maxWidth and maxHeight set to . infinity . The result of using .

How do you find the view size?

To calculate screen size: Measure the length and width of the screen in the same units. Multiply both numbers together to get the screen size - the area of the screen.

How do I resize a view in SwiftUI?

SwiftUI's scaleEffect() modifier lets us increase or decrease the size of a view freely. That makes the text view twice its regular size, scaled from the bottom-right corner. Tip: Scaling up a view won't cause it to be redrawn at its new size, only stretched up or down.

What is geometry reader?

A container view that defines its content as a function of its own size and coordinate space.


3 Answers

You could add an "overlay" using a GeometryReader to see the values. But in practice it would probably be better to use a "background" modifier and handle the sizing value discretely

struct Foo : View {
    var body: some View {
        Text("Hello World!")
            .font(.title)
            .foregroundColor(.white)
            .padding()
            .background(Color.red)
            .overlay(
                GeometryReader { proxy in
                    Text("\(proxy.size.width) x \(proxy.size.height)")
                }
            )
    }
}
like image 161
Jack Avatar answered Oct 24 '22 01:10

Jack


Printing out values is good, but being able to use them inside the parent view (or elsewhere) is better. So I took one more step to elaborate it.

struct GeometryGetter: View {
    @Binding var rect: CGRect

    var body: some View {
        GeometryReader { (g) -> Path in
            print("width: \(g.size.width), height: \(g.size.height)")
            DispatchQueue.main.async { // avoids warning: 'Modifying state during view update.' Doesn't look very reliable, but works.
                self.rect = g.frame(in: .global)
            }
            return Path() // could be some other dummy view
        }
    }
}

struct ContentView: View {
    @State private var rect1: CGRect = CGRect()
    var body: some View {
        HStack {
            // make to texts equal width, for example
            // this is not a good way to achieve this, just for demo
            Text("Long text").background(Color.blue).background(GeometryGetter(rect: $rect1))
            // You can then use rect in other places of your view:
            Text("text").frame(width: rect1.width, height: rect1.height).background(Color.green)
            Text("text").background(Color.yellow)
        }
    }
}
like image 34
Paul B Avatar answered Oct 23 '22 23:10

Paul B


Here is the ugly way I came up with to achieve this:

struct GeometryPrintingView: View {

    var body: some View {
        GeometryReader { geometry in
            return self.makeViewAndPrint(geometry: geometry)
        }
    }

    func makeViewAndPrint(geometry: GeometryProxy) -> Text {
        print(geometry.size)
        return Text("")
    }
}

And updated Foo version:

struct Foo : View {
    var body: some View {
        Text("Hello World!")
            .font(.title)
            .foregroundColor(.white)
            .padding()
            .background(Color.red)
            .overlay(GeometryPrintingView())
    }
}
like image 2
Russian Avatar answered Oct 23 '22 23:10

Russian