Responsive App Design Made Easy with Geometry Reader in SwiftUI

In the world of SwiftUI, creating responsive user interfaces is a breeze, thanks to powerful tools like GeometryReader. In this post, we’ll explore how to harness the potential of GeometryReader to build flexible and adaptive layouts. If you’re just starting out, this guide will help you unlock how to make a responsive UI design.

What is GeometryReader? GeometryReader is a SwiftUI container view that provides information about the size and position of its child views. It allows you to create layouts that adapt to various screen sizes and orientations. Essentially, it’s the cornerstone of responsive UI design in SwiftUI.

The Anatomy of GeometryReader: GeometryReader gives you access to a GeometryProxy, which contains information about the size and position of the view it’s applied to.

It provides properties like sizeframe, and global, enabling you to respond to changes in the parent view’s geometry.

Practical Use Cases: GeometryReader can be used in various scenarios, such as:

  • Creating adaptive layouts for different devices.
  • Handling text that needs to adjust its font size based on screen width.
  • Building dynamic grids or lists with variable spacing and content alignment.

Building a Responsive UI: Let’s dive into an example of using GeometryReader to create a responsive UI.

Imagine you want to display two Rectangle() views side by side, and their sizes should adapt to the available space.

Here’s how you can achieve it:

struct ContentView: View {
    var body: some View {
        GeometryReader { proxy in
            HStack {
                Rectangle()
                    .cornerRadius(20)
                    .foregroundColor(.blue)
                    .frame(width: proxy.size.width * 0.45, height: proxy.size.height)
                
                Rectangle()
                    .cornerRadius(20)
                    .foregroundColor(.yellow)
                    .frame(width: proxy.size.width * 0.45, height: proxy.size.height)
            }
            .padding()
        }
    }
}

In this example, the sizes of the rectangles are based on the available width within the GeometryReader. Also the height is set to use the whole height available on the device.


One more advanced way to accomplish the same goal:

When our views start to grow we will soon discover that reading it becomes something not that pleasant. Indeed adding new stuff to the same view can be very difficult so we need to look for solutions to make our code more readable.

So we can introduce GeometryReader in this way:

struct ContentView: View {
    @State var screenSize: CGSize = CGSize(width: 393, height: 839)
    
    var body: some View {
        HStack{
            Rectangle()
                .cornerRadius(20)
                .foregroundColor(.blue)
                .frame(width: screenSize.width * 0.45, height: screenSize.height)
            
            Rectangle()
                .cornerRadius(20)
                .foregroundColor(.yellow)
                .frame(width: screenSize.width * 0.45, height: screenSize.height)
        }
        .overlay(geometryReader)
    }
    
    var geometryReader: some View {
        GeometryReader { proxy in
            Color.clear
                .onAppear {
                    screenSize = proxy.size
                }
                .onChange(of: proxy.size) { oldVal , newVal in
                    screenSize = newVal
                }
        }
    }
}

At first glance it may seem more complex, but it’s not, we are decoupling some things here, let’s see.

Here we declare a State variable that changes whenever the new geometryReader variable changes.

As you can see here we can set an overlay to the the parent view where we set a Color.clear to make our calculations.

In this way we are simplifying our code and make in it more readable, also we leave the responsibility of calculating the current screen size to an independent component that we can move elsewhere simplifying the structure of our current view.

Conclusion: GeometryReader is a versatile tool that empowers SwiftUI developers to create responsive and adaptive user interfaces. Whether you’re working on a small iPhone screen or a spacious iPad, you can build layouts that adapt beautifully to any environment.

I love how fast can we build with SwiftUI and I enjoy writing about it.

Thanks for reading and let’s connect on Linkedin!