Imagine that you have a desk with a drawer. In the closed state, you can only see the table because the drawer is beneath it. In the opened state, you see the drawer which is below the desk (because it slides down). How would I animate this such that the drawer slides out from beneath the desk in SwiftUI?
This view will be work inside a list view, so a row's dimensions must accommodate any change. So I don't know if an offset based animation would work here (as shown in this question). The top view (the desk) and the bottom view (the drawer) are the same size.
Here's what I've tried...
In the code below, I use Rectangles but eventually I will use views.
Matched Geometry Effect
struct ContentView: View {
@Namespace private var animation
@State var shown: Bool = false
var body: some View {
Group {
if !shown {
ZStack {
Rectangle().frame(width: 300, height: 80, alignment: .center)
.foregroundColor(.blue)
.matchedGeometryEffect(id: "Bottom", in: animation)
Rectangle().frame(width: 300, height: 80, alignment: .center)
.matchedGeometryEffect(id: "Top", in: animation)
}
} else {
VStack(spacing: 0) {
Rectangle().frame(width: 300, height: 80, alignment: .center)
.matchedGeometryEffect(id: "Top", in: animation)
Rectangle().frame(width: 300, height: 80, alignment: .center)
.foregroundColor(.blue)
.matchedGeometryEffect(id: "Bottom", in: animation)
}
}
}.onTapGesture {
withAnimation(.linear(duration: 1)) {
shown.toggle()
}
}
}
}
Produces this...
Transitions
struct ContentView: View {
@State var shown: Bool = false
var body: some View {
VStack(spacing: 0) {
Rectangle().frame(width: 300, height: 80, alignment: .center)
.zIndex(0)
if shown {
Rectangle().frame(width: 300, height: 80, alignment: .center)
.foregroundColor(.blue)
.zIndex(-1)
.transition(.move(edge: .top))
}
}.onTapGesture {
withAnimation(.linear(duration: 1)) {
shown.toggle()
}
}
}
}