In this article we will be creating a card with a small animation using SwiftUI, turning that card into a component to organize our code, and then setting up Binding so that we can keep the animation we’ve created on the card.
Creating Components is very important, it’s what keeps your code clean and improves your apps performance. @Binding is used to link a specific state on a certain page.

The component view above is what we will build. We’ll begin by setting up the card itself. The first thing we want to do is add a ZStack that will host our card and act as the background of the page.
import SwiftUI
struct CodeSnippet: View {
var body: some View {
ZStack {
// Card Container & Background
}
}
}
struct CodeSnippet_Previews: PreviewProvider {
static var previews: some View {
CodeSnippet()
}
}
Next, we’ll add a `VStack`, which will be the actual card.
import SwiftUI
struct CodeSnippet: View {
var body: some View {
ZStack {
// Card Container & Background
VStack {
// Card
}
}
}
}
struct CodeSnippet_Previews: PreviewProvider {
static var previews: some View {
CodeSnippet()
}
}
Working within the VStack for the card’s content, we’ll next add an HStack to hold the title of our card and the icon we will animate later.
import SwiftUI
struct CodeSnippet: View {
var body: some View {
ZStack {
// Card Container & Background
VStack {
// Card
HStack {
// Top Card Content
}
}
}
}
}
struct CodeSnippet_Previews: PreviewProvider {
static var previews: some View {
CodeSnippet()
}
}
Now that we have our nested set of stacks, we’ll add a title Text and an icon Image to the HStack as well as a Text for the card’s description to the VStack. We’ll also set up a preview using a PreviewProvider.
This is what you should have so far:
import SwiftUI
struct CodeSnippet: View {
var body: some View {
ZStack {
// Card Container & Background
VStack {
// Card
HStack {
// Top Card Content
Text("Component Card")
Image(systemName: "bell.circle")
}
Text("This is a description for the iOS Card we're building")
}
}
}
}
struct CodeSnippet_Previews: PreviewProvider {
static var previews: some View {
CodeSnippet()
}
}
Styling the Card
The contents of our card are in place, but it doesn’t look like much. We’ll add some design to our code next to make it look like a card. First, let’s add one background color to the page and another background color to our card.
import SwiftUI
struct CodeSnippet: View {
var body: some View {
ZStack {
// Card Container & Background
Color.gray.opacity(0.1)
.edgesIgnoringSafeArea(.all)
VStack {
// Card
HStack {
// Top Card Content
Text("Component Card")
Image(systemName: "bell.circle")
}
Text("This is a description for the iOS Card we're building")
}
.background(Color.white)
}
}
}
struct CodeSnippet_Previews: PreviewProvider {
static var previews: some View {
CodeSnippet()
}
}
Here we added a Color.gray.opacity(0.1) modifier to our ZStack along with an .edgesIgnoringSafeArea(.all) modifier so that the Color can take up the whole background. We also added a .background(Color.white) modifier to the card.
Let’s further refine the design of our card by adding .frame and .padding modifiers.
import SwiftUI
struct CodeSnippet: View {
var body: some View {
ZStack {
// Card Container & Background
Color.gray.opacity(0.1)
.edgesIgnoringSafeArea(.all)
VStack {
// Card
HStack {
// Top Card Content
Text("Component Card")
Image(systemName: "bell.circle")
}
Text("This is a description for the iOS Card we're building")
}
.frame(maxWidth: .infinity)
.frame(height: 200)
.background(Color.white)
.padding(.horizontal, 20)
}
}
}
struct CodeSnippet_Previews: PreviewProvider {
static var previews: some View {
CodeSnippet()
}
}
Our design is starting to come together! The next thing to do is to start styling our text and the icon inside the card.
VStack {
// Card
HStack {
// Top Card Content
Text("Component Card")
.font(.system(size: 28, weight: .bold))
Spacer()
Image(systemName: "bell.circle")
.font(.system(size: 28))
.foregroundColor(.blue)
}
Text("This is a description for the iOS Card we're building")
.font(.system(size: 22, weight: .medium))
.foregroundColor(.gray)
.padding(.horizontal, 4)
.padding(.top, 4)
}
We’ve formatted the text and the icon, and we used a Spacer inside the HStack to separate the text and the icon.
Now that the contents of the card are looking good, let’s add a couple styles to the card itself.
...
}
.frame(maxWidth: .infinity)
.frame(height: 200)
.background(Color.white)
.mask(RoundedRectangle(cornerRadius: 30))
.shadow(color: Color.black.opacity(0.05), radius: 30, y:15)
.padding(.horizontal, 20)
}
}
}
We’ve added .mask and .shadow modifiers to the VStack that represents our card.
Our design is complete! Now let’s dive into state.
Using State for Animations
We will be adding an animation for when the user taps the notification icon. First, we need to create a state object to control the animation.
struct CodeSnippet: View {
@State var notifications = false
var body: some View {
ZStack {
// Card Container & Background
Color.gray.opacity(0.1)
.edgesIgnoringSafeArea(.all)
}
Next we have to set up what happens when we toggle this state. We’re going to add notifications ? “bell.circle.fill” : “bell.circle” to the Image that represents our notification icon. When a user toggles the notification state, the bell icon will change from a stroke icon to a filled icon. You will see this come to life when we add the .onTapGesture to our image next.
HStack {
// Top Card Content
Text("Component Card")
.font(.system(size: 28, weight: .bold))
Spacer()
Image(systemName: notifications ? "bell.circle.fill ": "bell.circle")
.font(.system(size: 28))
.foregroundColor(.blue)
}
Now let’s apply the .onTapGesture modifier.
HStack {
// Top Card Content
Text("Component Card")
.font(.system(size: 28, weight: .bold))
Spacer()
Image(systemName: notifications ? "bell.circle.fill ": "bell.circle")
.font(.system(size: 28))
.foregroundColor(.blue)
.onTapGesture {
notifications.toggle()
}
}
Tap play on your preview, then tap the notification icon. You will see it change from stroked to filled.
Congratulations! We’ve successfully designed a card using SwiftUI, added state to our card, and added an onTapGesture to toggle this state. The last thing we’ll do is turn the card into a component and bind the state we just created to the component.
To make these changes, we are going to move our VStack code into a new file. Create a new group in your Xcode file called Components. Add a new file into that group called Card.
Back in our original file, let’s cut the VStack code so we can paste it in the new file. To make this easier, Command-click on the first VStack bracket and select “Fold.” Your code should look like this:
VStack {
// Card
HStack {
// Top Card Content
Text("Component Card")
.font(.system(size: 28, weight: .bold))
Spacer()
Image(systemName: notifications ? "bell.circle.fill ": "bell.circle")
.font(.system(size: 28))
.foregroundColor(.blue)
.onTapGesture {
notifications.toggle()
}
}
Text("This is a description for the iOS Card we're building")
.font(.system(size: 22, weight: .medium))
.foregroundColor(.gray)
.padding(.horizontal, 4)
.padding(.top, 4)
}
.frame(maxWidth: .infinity)
.frame(height: 200)
.background(Color.white)
.mask(RoundedRectangle(cornerRadius: 30))
.shadow(color: Color.black.opacity(0.05), radius: 30, y:15)
.padding(.horizontal, 20)
}
}
}
Now highlight the VStack code, cut it, and paste it into the new Card file you just created. Your new Card file should look something like this. Don’t worry - we will be fixing the errors you see in a moment, with just one line of code.

To fix these errors, all we have to do is add Binding for our notification state to this new Card file. Here’s what it should look like:
import SwiftUI
struct Card: View {
@Binding var notifications: Bool
var body: some View {
VStack {
// Card
HStack {
// Top Card Content
Text("Component Card")
.font(.system(size: 28, weight: .bold))
Spacer()
Image(systemName: notifications ? "bell.circle.fill ": "bell.circle")
.font(.system(size: 28))
.foregroundColor(.blue)
.onTapGesture {
notifications.toggle()
}
}
Text("This is a description for the iOS Card we're building")
.font(.system(size: 22, weight: .medium))
.foregroundColor(.gray)
.padding(.horizontal, 4)
.padding(.top, 4)
}
.frame(maxWidth: .infinity)
.frame(height: 200)
.background(Color.white)
.mask(RoundedRectangle(cornerRadius: 30))
.shadow(color: Color.black.opacity(0.05), radius: 30, y:15)
.padding(.horizontal, 20)
}
}
And that’s it! We’ve successfully designed a card using SwiftUI, turned the card into a component, and added state and binding.
I hope this tutorial was helpful. Thank you for tuning in!