New in SwiftUI 4 : AnyLayout

DevTechie Inc
Jun 27, 2022


Photo by Hal Gatewood on Unsplash

Introduced in SwiftUI 4, AnyLayout is a type-erased instance of the layout protocol. We can use AnyLayout instance to enable dynamic layout container change without destroying the state of subviews. For example, if we a VStack displaying two subviews in portrait mode, with the toggle of a button we can change the VStack to HStack without affecting current state of subviews.

Let’s explore with an example. We will create State variable to toggle layout upon tap gesture on the view. We will also create a layout variable which will be of type AnyLayout for VStack or HStack, depending upon the current state of toggleLayoutView. 

struct ContentView: View {
    
    @State private var toggleLayout = false
    
    var body: some View {
        
        let layout = toggleLayout ? AnyLayout(HStack()) : AnyLayout(VStack())
        
        layout {
            Text("DevTechie.com")
                .font(.largeTitle)
                .foregroundColor(.primary)
            Image(systemName: "swift")
                .font(.largeTitle)
        }
        .padding()
        .onTapGesture {
            toggleLayout.toggle()
        }
    }
}
AnyLayout supports animation so the change can be animated by wrapping toggleLayout.toggle line inside withAnimation block.

struct ContentView: View {
    
    @State private var toggleLayout = false
    
    var body: some View {
        
        let layout = toggleLayout ? AnyLayout(HStack()) : AnyLayout(VStack())
        
        layout {
            Text("DevTechie.com")
                .font(.largeTitle)
                .foregroundColor(.primary)
            Image(systemName: "swift")
                .font(.largeTitle)
        }
        .padding()
        .onTapGesture {
            withAnimation {
                toggleLayout.toggle()
            }
        }
    }
}
We will add a Button to the view to see that state of the subviews are preserved. Upon tapping on the button, we will change color of subviews and then we will toggle the layout, notice that the color retains its state.

struct ContentView: View {
    
    @State private var toggleLayout = false
    @State private var themeColor = Color.blue
    
    var body: some View {
        
        let layout = toggleLayout ? AnyLayout(HStack()) : AnyLayout(VStack())
        
        VStack {
            layout {
                Text("DevTechie.com")
                    .font(.largeTitle)
                    
                Image(systemName: "swift")
                    .font(.largeTitle)
            }
            .foregroundColor(themeColor)
            .padding()
            .onTapGesture {
                withAnimation {
                    toggleLayout.toggle()
                }
            }
            
            Button("Change color") {
                themeColor = themeColor == .blue ? .orange : .blue
            }
        }
    }
}
Another way to toggle layout change can be environment variables. Let’s place an environment variable to observe size class change and adjust layout based upon that.

struct ContentView: View {
    
    @Environment(\.verticalSizeClass) var vSizeClass
    @State private var themeColor = Color.blue
    
    var body: some View {
        
        let layout = vSizeClass == .compact ? AnyLayout(HStack()) : AnyLayout(VStack())
        
        VStack {
            layout {
                Text("DevTechie.com")
                    .font(.largeTitle)
                    
                Image(systemName: "swift")
                    .font(.largeTitle)
            }
            .foregroundColor(themeColor)
            .padding()
            
            Button("Change color") {
                themeColor = themeColor == .blue ? .orange : .blue
            }
        }
    }
}


With that we have reached the end of this article. Thank you once again for reading. Subscribe our newsletter at https://www.devtechie.com