Animatable
Yet another animation modifiers (like fireworks, live comments or explosion) on SwiftUI
Install / Use
/learn @c-villain/AnimatableREADME
Animatable
Yet another animation modifiers for buttons, skeletons and other views on pure SwiftUI.
<p align="left"> <img src="Gifs/quick_single.gif" alt="" width="300"> </p> <p align="left"> <img src="Gifs/skeletons.gif" alt="" width="300"> </p>Demo example for card skeletons
<p align="left"> <img src="Gifs/card_skeleton_example.gif" alt="" width="300"> </p>🔍 Full demo video you can find here.
👨🏻💻 Feel free to subscribe to channel SwiftUI dev in telegram.
Requirements
- iOS 13.0 or macOS 10.15
Installation
Swift Package Manager
To integrate Animatable into your project using SwiftPM add the following to your Package.swift:
dependencies: [
.package(url: "https://github.com/c-villain/Animatable", from: "0.1.0"),
],
or via XcodeGen insert into your project.yml:
name: YourProjectName
options:
deploymentTarget:
iOS: 13.0
packages:
Animatable:
url: https://github.com/c-villain/Animatable
from: 0.1.0
targets:
YourTarget:
type: application
...
dependencies:
- package: Animatable
Quick start
All examples you can find in demo project inside package.
There are different custom animation types provided by Animatable.
👇🏻 Tap on its name to see description and example of using.
<details> <summary>Live comments effect</summary> <p align="left"> <img src="Gifs/liveComments.gif" alt="live comments"> </p>Use .animate(.liveComments(stamps:),animate:) where stamps is number of prints in animation activity, animate is flag to start animation.
@State var animate: Bool = false
...
Button {
animate.toggle()
} label: {
HStack(spacing: 8) {
Image(systemName: animate ? "heart.fill" : "heart")
.resizable()
.scaledToFit()
.animate(.liveComments(stamps: 4),
animate: animate)
.frame(width: 24, height: 24)
.foregroundColor(.white)
Text("Like")
.font(.body)
.fontWeight(.medium)
.foregroundColor(.white)
}
.padding(12)
.background(
Rectangle()
.fill(.pink.opacity(0.8))
.cornerRadius(12)
)
}
</details>
<details>
<summary>Explosion effect</summary>
<p align="left">
<img src="Gifs/explosion.gif" alt="explosion">
</p>
Use .animate(.explosive(color:),animate:) where color is color of explosion in animation activity, animate is flag to start animation.
@State var animate: Bool = false
...
Button {
animate.toggle()
} label: {
HStack(spacing: 8) {
Image(systemName: animate ? "power" : "poweroff")
.resizable()
.scaledToFit()
.animate(.explosive(color: .white),
animate: animate)
.frame(width: 24, height: 24)
.foregroundColor(.white)
Text(animate ? "On" : "Off")
.font(.body)
.fontWeight(.medium)
.foregroundColor(.white)
}
.padding(12)
.background(
Rectangle()
.fill(.gray.opacity(0.8))
.cornerRadius(12)
)
}
</details>
<details>
<summary>Tweak effect</summary>
<p align="left">
<img src="Gifs/tweak.gif" alt="tweak">
</p>
Use .animate(.tweaking(amount:,shakesPerUnit:),animate:) where amount is tweak offset, shakesPerUnit is number of shakes in tweking, animate is flag to start animation.
@State var animate: Bool = false
...
Button {
animate.toggle()
} label: {
HStack(spacing: 8) {
Image(systemName: animate ? "hand.thumbsup.fill" : "hand.thumbsup")
.resizable()
.scaledToFit()
.animate(.tweaking(),
animate: animate)
.frame(width: 24, height: 24)
.foregroundColor(.white)
Text("Like")
.font(.body)
.fontWeight(.medium)
.foregroundColor(.white)
}
.padding(12)
.background(
Rectangle()
.fill(.blue.opacity(0.8))
.cornerRadius(12)
)
}
</details>
<details>
<summary>Scaling effect</summary>
<p align="left">
<img src="Gifs/scaling.gif" alt="scaling">
</p>
Use .animate(.scaling(scaling:),animate:) where scaling is scaling factor, animate is flag to start animation.
@State var animate: Bool = false
...
Button {
animate.toggle()
} label: {
HStack(spacing: 8) {
Image(systemName: animate ? "plus.app.fill" : "plus.app")
.resizable()
.scaledToFit()
.animate(.scaling(),
animate: animate)
.frame(width: 24, height: 24)
.foregroundColor(.white)
Text("Add")
.font(.body)
.fontWeight(.medium)
.foregroundColor(.white)
}
.padding(12)
.background(
Rectangle()
.fill(.yellow.opacity(0.8))
.cornerRadius(12)
)
}
</details>
<details>
<summary>Rotating effect</summary>
<p align="left">
<img src="Gifs/rotating.gif" alt="scaling">
</p>
Use .animate(.rotating,animate:) where animate is flag to start animation.
@State var animate: Bool = false
...
Button {
animate.toggle()
} label: {
HStack(spacing: 8) {
Image(systemName: animate ? "arrow.triangle.2.circlepath.circle.fill" : "arrow.triangle.2.circlepath.circle")
.resizable()
.scaledToFit()
.animate(.rotating,
animate: animate)
.frame(width: 24, height: 24)
.foregroundColor(.white)
Text("Sync")
.font(.body)
.fontWeight(.medium)
.foregroundColor(.white)
}
.padding(12)
.background(
Rectangle()
.fill(.blue.opacity(0.8))
.cornerRadius(12)
)
}
</details>
<details>
<summary>Fireworks effect</summary>
<p align="left">
<img src="Gifs/fireworks.gif" alt="fireworks">
</p>
Use .animate(.fireworks(color:),animate:) where color is color of animation, animate is flag to start animation.
@State var animate: Bool = false
...
Button {
animate.toggle()
} label: {
HStack(spacing: 8) {
Image(systemName: animate ? "sun.max.fill" : "sun.max")
.resizable()
.scaledToFit()
.animate(.fireworks(color: .white),
animate: animate)
.frame(width: 24, height: 24)
.foregroundColor(.white)
Text("Weather")
.font(.body)
.fontWeight(.medium)
.foregroundColor(.white)
}
.padding(12)
.background(
Rectangle()
.fill(.blue.opacity(0.8))
.cornerRadius(12)
)
}
</details>
👇🏻 You can easily join them together to combine animation.
<details> <summary>Combining animation</summary> <p align="left"> <img src="Gifs/combined.gif" alt="combined"> </p>Use sequence of .animate(type:,animate:) to get multiple animation effect.
@State var animate: Bool = false
...
Button {
animate.toggle()
} label: {
HStack(spacing: 8) {
Image(systemName: animate ? "sun.max.fill" : "sun.max")
.resizable()
.scaledToFit()
.animate(.rotating,
animate: animate)
.animate(.explosive(color: .red, factor: 2.0),
animate: animate)
.animate(.explosive(color: .blue, factor: 1.4),
animate: animate)
.animate(.fireworks(color: .yellow, factor: 3.5),
animate: animate)
.frame(width: 24, height: 24)
.foregroundColor(.red)
Text("Combined")
.font(.body)
.fontWeight(.medium)
.foregroundColor(.white)
}
.padding(12)
.background(
Rectangle()
.fill(.blue.opacity(0.6))
.cornerRadius(12)
)
}
</details>
👇🏻 Animation for skeletons and other views.
<details> <summary>Shimmers</summary> <p align="left"> <img src="Gifs/shimmer.gif" alt="combined"> </p>Use sequence of .shimmerable(configuration:) where configuration is settings for for shimmers or .shimmerable() for defaults
Rectangle()
.fill(.pink.opacity(0.8))
.frame(height: 40)
.frame(maxWidth: .infinity)
.shimmerable()
.cornerRadius(12)
</details>
<details>
<summary>Blinking</summary>
<p align="left">
<img src="Gifs/blinking.gif" alt="combined">
</p>
Use sequence of .blinking(configuration:) where configuration is settings for for blinking animation or .blinking() for de
Related Skills
node-connect
353.3kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
111.7kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
353.3kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
353.3kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
