SwiftUI Transforms and Animations

Episode #399 | 11 minutes | published on July 11, 2019 | Uses Xcode-11.0-beta3, Swift-5.1
Free Video
SwiftUI's declarative nature makes building UIs incredibly easy. In this episode we will build a wallet UI with cards. We will create a CardView so we can reuse it in multiple places. Then we will use transforms to alter it's size and position. Finally we will see how declarative animations work as we expand the cards apart.
Apologies for the audio in this episode. I was not in my normal recording environment.

Creating Card View

To build a wallet UI, we will start by creating a card view using a VStack. We want to use the same view multiple times so we will extract the contents into a new view typed called CardView. We can extract the title and color into properties so they can be passed in.


struct ContentView : View {

    @State var expanded: Bool = false

    var body: some View {
        ZStack {
            CardView(title: "Walmart", color: .blue)
                .offset(x: 0, y: expanded ? -400 : -40)
                .scaleEffect(expanded ? 1 : 0.90)
            CardView(title: "Target", color: .red)
                .offset(x: 0, y: expanded ? -200 : -20)
                .scaleEffect(expanded ? 1 : 0.95)
            CardView(title: " Card", color: .black)
                .tapAction {
                    self.expanded.toggle()
                }
        }
        .offset(x: 0, y: expanded ? 150 : 0)
        .animation(.spring(mass: 1.0, stiffness: 100, damping: 20, initialVelocity: 0))
    }
}

struct CardView : View {
    let title: String
    let color: Color

    var body: some View {
        ZStack {
            Rectangle()
                .fill(color)
                .cornerRadius(10)
                .frame(width: 320, height: 210)

            Text(title)
                .font(.largeTitle)
                .bold()
                .foregroundColor(.white)
        }.shadow(radius: 6)
    }
}

To make the cards collapse on top of each other we will change the view to ZStack.

Adding Transforms

Currently the cards are all on top of each other so you can only see the top card. We can utilize transforms to nudge the cards up and give them a smaller size to give a sense of depth.

To move the cards slightly up we will set the .offset to the cards placed below the top card. We will also reduce the card size with .scaleEffect.

@State var expanded: Bool = false

var body: some View {
    ZStack {
        CardView(title: "Walmart", color: .blue)
            .offset(x: 0, y: -40)
            .scaleEffect(0.90)
        CardView(title: "Target", color: .red)
            .offset(x: 0, y: -20)
            .scaleEffect(0.95)
        CardView(title: " Card", color: .black)
    }
    .offset(x: 0, y: 0)
}

Adding Declarative Animation

To see the card expanding once clicked on them we will first create a boolean @State property wrapper to indicate the state of the view (expanded or not). Next we create a .tapAction on the top card. This will help us to toggle the boolean variable.

Next we use this variable to inspect the expanded state in and choose a value for .offset and .scaleEffect. Now we will add a fun animation using .animation with a .spring animation style.

We can tweak the spring parameters to give a more realistic looking animation and control how bouncy it feels.

@State var expanded: Bool = false

var body: some View {
    ZStack {
        CardView(title: "Walmart", color: .blue)
            .offset(x: 0, y: expanded ? -400 : -40)
            .scaleEffect(expanded ? 1 : 0.90)
        CardView(title: "Target", color: .red)
            .offset(x: 0, y: expanded ? -200 : -20)
            .scaleEffect(expanded ? 1 : 0.95)
        CardView(title: " Card", color: .black)
            .tapAction {
                self.expanded.toggle()
            }
    }
    .offset(x: 0, y: expanded ? 150 : 0)
    .animation(.spring(mass: 1.0, stiffness: 100, damping: 20, initialVelocity: 0))
}   
blog comments powered by Disqus