I’m making a simple task app and using ForEach to populate task rows with the task information from my model. I need a way to animate my task view to open up and reveal some description text and two buttons. I want to turn from A into B on tap, and then back again on tap:
I’ve tried a couple things. I successfully got a proof-of-concept rectangle animating in a test project, but there are issues. The rectangle shrinks and grows from the centre point, vs. from the bottom only. When I place text inside it, the text doesn’t get hidden and it looks really bad.
struct ContentView: View {
@State var animate = false
var animation: Animation = .spring()
var body: some View {
VStack {
Rectangle()
.frame(width: 200, height: animate ? 60 : 300)
.foregroundColor(.blue)
.onTapGesture {
withAnimation(animation) {
animate.toggle()
}
}
}
}
In my main app, I was able to replace my first task view (closed) with another view that’s open. This works but it looks bad and it’s not really doing what I want. It’s effectively replacing the view with another one using a fade animation.
ForEach(taskArrayHigh) { task in
if animate == false {
TaskView(taskTitle: task.title, category: task.category?.rawValue ?? "", complete: task.complete?.rawValue ?? "", priorityColor: Color("HighPriority"), task: task, activeDate: activeDate)
.padding(.top, 10)
.padding(.horizontal)
.onTapGesture {
withAnimation(.easeIn) {
animate.toggle()
}
}
.transition(.move(edge: .bottom))
} else if animate == true {
TaskViewOpen(task: "Grocery Shopping", category: "Home", remaining: 204, completed: 4)
.padding(.top, 10)
.padding(.horizontal)
.onTapGesture {
withAnimation(.easeIn) {
animate.toggle()
}
}
}
Is there a way to animate my original closed view to open up and reveal the description text and buttons?
2
Answers
You are on the right track with your
.transition
line you have, but you want to make sure that the container stays the same and the contents change — right now, you’re replacing the entire view.Here’s a simple example illustrating the concept:
Since you’re using it inside a
ForEach
, you’ll probably want to abstract this into its own component, as it’ll need its own@State
to keep track of the expanded state as I’ve shown here.Update, based on comments:
Example of using a PreferenceKey to get the height of the expandable view so that the frame can be animated and nothing fades in and out:
Using Swift 5 you can use withAnimation and have the view hidden based on state.
ExpandViewer
Using the viewer