Dynamically adapting to available space with ViewThatFits

Dynamically adapting to available space with ViewThatFits

Learn how to create views that adapt their size in order to fill the available space on the UI with SwiftUI.

ViewThatFits is a SwiftUI component introduced from iOS 16 that allows your view to become responsive according to the available space that will contain it.

By providing a series of possible views to display - ordered by preference, the component will use the @ViewBuilder to build the first one that fits the space available.

This is very useful when the parent’s container size is not fixed and you want the child view to adapt based on that size.

ViewThatFits(in: .vertical) {
    //First Option
    VStack {
        Image(systemName: "swift")
            .imageScale(.large)
            .foregroundStyle(.tint)
        
        Text("We can’t wait to see what you will Create with Swift.")
    }
    .font(.system(size: 45))
    .fontWeight(.ultraLight)
    .frame(width: 350, height: 350)
    
    //Second Option
    Text("Create with Swift")
        .font(.system(size: 45))
        .fontWeight(.ultraLight)
    
    //Third Option
    Text("Swift")
        .font(.system(size: 40))
        .fontWeight(.ultraLight)
    
    //Fourth Option
    Image(systemName: "swift")
        .foregroundStyle(.tint)
        .font(.system(size: 40))
}

To begin using ViewThatFits, declare an instance of the component. It takes two parameters:

  1. The axis used to fill. By default, it uses both axes, while if specified, it chooses the first child whose size fits within the proposed size on that axis;
  2. The content is to be displayed in the view.

In the example above, ViewThatFits works on the vertical axis and takes four different views ordered by preference.

Integrated with the parent view, it will look like this:

import SwiftUI

struct ContentView: View {
    
    @State private var frameSize: CGFloat = 350
    @State private var isEditing = false
    
    var body: some View {
        VStack {
            Spacer()
            VStack {
                ViewThatFits(in: .vertical) {
                    //First Option
                    VStack {
                        Image(systemName: "swift")
                            .imageScale(.large)
                            .foregroundStyle(.tint)
                        
                        Text("We can’t wait to see what you will Create with Swift.")
                    }
                    .font(.system(size: 45))
                    
                    //Second Option
                    Text("Create with Swift")
                        .font(.system(size: 45))
                    
                    //Third Option
                    Text("Swift")
                    
                    //Fourth Option
                    Image(systemName: "swift")
                        .foregroundStyle(.tint)
                }
            }
            .fontWeight(.ultraLight)
            .font(.system(size: 40))
            .frame(width: frameSize, height: frameSize)
            
            .overlay {
                RoundedRectangle(cornerSize: CGSize(width: 12, height: 12))
                    .stroke(Color.blue, lineWidth: 1)
            }
            
            Spacer()
            
            Slider(value: $frameSize, in: 60...350)  { isEditing in
                self.isEditing = isEditing
            }
            
            Text("Frame size: \(Int(frameSize))")
        }
        .padding()
    }
}

This SwiftUI view dynamically adjusts a ViewThatFits container with fourth layouts: an icon and text, just text in 2 different versions, or just an icon, based on the frame size controlled by a slider. A blue-bordered square resizes with the slider, showcasing how the layout adapts. The current size is displayed below the slider for reference.

0:00
/0:23

As you can see, ViewThatFits allows to have control on how your view is adapting based on the changing in size of the container that will contains it, making it ideal for adaptable UIs where container sizes vary.