Switching a container with an if in the body destroys and recreates the children, losing their state. AnyLayout (iOS 16) swaps the layout while keeping the views' structural identity, so state survives and the move animates.
The Layout Protocol in One Look
Every container now conforms to Layout: sizeThatFits computes the container's size, placeSubviews positions the children. SwiftUI ships layout twins of the stacks: HStackLayout, VStackLayout, ZStackLayout, and GridLayout. Apple recommends the twins only for conditional layouts; keep using HStack and friends otherwise.
Why the if Statement Loses State
Branching in a ViewBuilder changes the structural identity, so SwiftUI tears the subtree down and clears its state when the branch flips.
// state inside View1/View2 resets when the size class changes
if size == .regular {
HStack { View1(); View2() }
} else {
VStack { View1(); View2() }
}
AnyLayout Keeps Identity
Erase the layout's type and call it like a container. The children stay the same views; only their placement changes, and the transition animates.
struct ConditionalLayoutExample: View {
@Environment(\.horizontalSizeClass) private var size
var body: some View {
let layout = (size == .regular)
? AnyLayout(HStackLayout())
: AnyLayout(VStackLayout())
layout {
View1()
View2()
}
.animation(.default, value: size)
}
}
Layout and AnyLayout are iOS 16, macOS 13, tvOS 16, and watchOS 9 and up.