Responsive App Design Made Easy with Geometry Reader in SwiftUI

Start using 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.


How GeometryReader works

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.

When you create a GeometryReader inside your views it looks something like this:

GeometryReader { proxy in
  // Now we have access to various sizes throughout the proxy
  proxy.size.width 
  proxy.size.height
  
}   

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 see 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.


Another way to implement GeometryReader

One more advanced way to use it

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.

We use an overlay to the the parent view where we set a Color.clear and we use it 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.

Thanks for reading!


Browse more categories

TOC