c-villain/Animatable
Yet another animation modifiers (like fireworks, live comments or explosion) on SwiftUI
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: AnimatableQuick 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.
```swift
@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.
```swift
@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.
```swift
@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.
```swift
@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.
```swift
@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.
```swift
@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.
```swift
@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
```swift
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 defaults
```swift
Rectangle()
.fill(.pink.opacity(0.8))
.frame(height: 40)
.frame(maxWidth: .infinity)
.blinking()
.cornerRadius(12)
```
</details>
<details>
<summary>Combining blinking and shimmerable</summary>
<p align="left">
<img src="Gifs/combined_skeleton.gif" alt="combined">
</p>
Use sequence of `.blinking()` and `.shimmerable()` to get multiple animation effect.
```swift
Rectangle()
.fill(.pink.opacity(0.8))
.frame(height: 40)
.frame(maxWidth: .infinity)
.blinking()
.shimmerable()
.cornerRadius(12)
```
</details>
### β οΈ Recommendations for use
<details>
<summary>with combining animation</summary>
Order in sequence of `.animate(type:,animate:)` is really important!
Feel the difference in the next example:
```swift
@State var animate: Bool = false
...
Button {
animate.toggle()
} label: {
HStack(spacing: 8) {
Image(systemName: multiple ? "sun.max.fill" : "sun.max")
.resizable()
.scaledToFit()
.animate(.liveComments(stamps: 4),
animate: animate)
.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.0),
animate: animate)
.frame(width: 24, height: 24)
.foregroundColor(.red)
Text("Weather")
.font(.body)
.fontWeight(.medium)
.foregroundColor(.white)
}
.padding(12)
.background(
Rectangle()
.fill(.blue.opacity(0.6))
.cornerRadius(12)
)
}
```
Using this sequence of `.animate(...)` leads to such behaviour:
<p align="left">
<img src="Gifs/problem1.gif" alt="problem1">
</p>
To get expected behaviour this we should change the order in chain:
```swift
@State var animate: Bool = false
...
Button {
animate.toggle()
} label: {
HStack(spacing: 8) {
Image(systemName: multiple ? "sun.max.fill" : "sun.max")
.resizable()
.scaledToFit()
.animate(.rotating, // <== Look here!
animate: animate)
.animate(.liveComments(stamps: 4), // <== Look here!
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.0),
animate: animate)
.frame(width: 24, height: 24)
.foregroundColor(.red)
Text("Weather")
.font(.body)
.fontWeight(.medium)
.foregroundColor(.white)
}
.padding(12)
.background(
Rectangle()
.fill(.blue.opacity(0.6))
.cornerRadius(12)
)
}
```
The result:
<p align="left">
<img src="Gifs/fix1.gif" alt="erro1">
</p>
</details>
<details>
<summary>with group of views</summary>
Use can use `.animate(...)` not only for one view but for group of views
```swift
@State var animate: Bool = false
...
Button {
animate.toggle()
} label: {
HStack(spacing: 8) {
Image(systemName: animate ? "heart.fill" : "heart")
.resizable()
.scaledToFit()
.frame(width: 24, height: 24)
.foregroundColor(.red)
Text("Like")
.font(.body)
.fontWeight(.medium)
.foregroundColor(.red)
}
.animate(.liveComments(stamps: 4), // <== Look here!
animate: animate)
.padding(12)
.background(
Rectangle()
.fill(.blue.opacity(0.8))
.cornerRadius(12)
)
}
```
The result:
<p align="left">
<img src="Gifs/example1.gif" alt="example1">
</p>
</details>
</details>
<details>
<summary>with standart SUI modifiers</summary>
Be careful with standard SUI modifiers. It may cause different side effects.
For example [`cornerRadius`](https://developer.apple.com/documentation/quartzcore/calayer/1410818-cornerradius) crops the modified view.
```swift
@State var animate: Bool = false
...
Button {
animate.toggle()
} label: {
HStack(spacing: 8) {
Image(systemName: liveComments ? "heart.fill" : "heart")
.resizable()
.scaledToFit()
.animate(.liveComments(stamps: 4),
animate: animate)
.frame(width: 24, height: 24)
.foregroundColor(.red)
Text("Like")
.font(.body)
.fontWeight(.medium)
.foregroundColor(.white)
}
.padding(12)
.background (
Color.blue.opacity(0.8)
)
.cornerRadius(12) // <== Look here!
}
```
The result:
<p align="left">
<img src="Gifs/problem2.gif" alt="problem2">
</p>
I recommend you to use it in the `.background` to get expected behaviour:
```swift
.background ( // <== Look here
Rectangle()
.fill(.blue.opacity(0.8))
.cornerRadius(12) // <== Look here
)
```
</details>Communication
- If you like this repository, please do :star: to make this useful for others.
- If you found a bug, open an issue or submit a fix via a pull request.
- If you have a feature request, open an issue or submit a implementation via a pull request or hit me up on lexkraev@gmail.com or telegram.
- If you want to contribute, submit a pull request onto the master branch.
License
Animatable package is released under an MIT license.
Package Metadata
Repository: c-villain/Animatable
Homepage: https://t.me/swiftui_dev
Stars: 58
Forks: 4
Open issues: 0
Default branch: main
Primary language: swift
License: MIT
Topics: blinking, ios, shimmers, swift-package, swiftui, swiftui-animation, swiftui-animations, swiftui-example
README: README.md