dependencies: [
.package(url: "https://github.com/NuPlay/AnimationKit.git", .upToNextMinor(from: "1.0.0"))
]
import SwiftUI
import AnimationKit
struct AnimationKit_Test: View {
var body: some View {
VStack {
AnimatedStack(preset: .default) {
Text("Hello, World!")
.font(.title)
Text("This is Simple Stack View")
.font(.title2)
Rectangle()
.fill(Color.blue)
.frame(width: 100, height: 100)
}
}
}
}
import SwiftUI
import AnimationKit
struct AnimationKit_Test: View {
@State private var emojiData: [String] = ["π", "πΈ", "π°", "π", "π ", "π", "π", "π", "πͺ", "π", "π", "π", "π", "π", "π", "π", "π", "π", "π", "π"]
var body: some View {
ScrollView {
LazyVStack {
AnimatedForEach(emojiData, preset: .list) { emoji in
Text(emoji)
.font(.system(size: 100))
}
}
}
}
}
import SwiftUI
import AnimationKit
struct AnimationKitGrid_Test: View {
@State private var emojiData: [String] = ["π", "πΈ", "π°", "π", "π ", "π", "π", "π", "πͺ", "π", "π", "π", "π", "π", "π", "π", "π", "π", "π", "π"]
let columns = [GridItem(.adaptive(minimum: 100))]
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 20) {
AnimatedForEach(emojiData, preset: .grid) { emoji in
Text(emoji)
.font(.system(size: 100))
}
}
.padding()
}
}
}
public protocol AnimatableStyle {
var opacity: AnimationValue<CGFloat> { get }
var offsetX: AnimationValue<CGFloat> { get }
var offsetY: AnimationValue<CGFloat> { get }
var rotation: AnimationValue<Double> { get }
var scale: AnimationValue<Double> { get }
var blur: AnimationValue<Double> { get }
var delay: Double { get }
var animation: Animation { get }
var maxAnimationCount: Int { get }
}
struct YourCustomAnimationConfig: AnimatableStyle {
var opacity: AnimationValue<CGFloat> = .init(from: 0, to: 1)
var offsetX: AnimationValue<CGFloat> = .init(from: 0, to: 0)
var offsetY: AnimationValue<CGFloat> = .init(from: 50, to: 0)
var rotation: AnimationValue<Double> = .init(from: 0, to: 0)
var scale: AnimationValue<Double> = .init(from: 1, to: 1)
var blur: AnimationValue<Double> = .init(from: 8, to: 0)
var delay: Double = 0.05
var animation: Animation = .bouncy(duration: 0.4)
var maxAnimationCount: Int = 20
}
AnimatedForEach(emojiData, preset: YourCustomAnimationConfig()) { emoji in
}
public struct AnimationConfig: Hashable, Equatable, AnimatableStyle {
public var opacity: AnimationValue<CGFloat>
public var offsetX: AnimationValue<CGFloat>
public var offsetY: AnimationValue<CGFloat>
public var rotation: AnimationValue<Double>
public var scale: AnimationValue<Double>
public var blur: AnimationValue<Double>
public var delay: Double
public var animation: Animation
public var maxAnimationCount: Int
public init(
opacity: AnimationValue<CGFloat>,
offsetX: AnimationValue<CGFloat>,
offsetY: AnimationValue<CGFloat>,
rotation: AnimationValue<Double>,
scale: AnimationValue<Double>,
blur: AnimationValue<Double>,
delay: Double = 0.05,
animation: Animation = .bouncy(duration: 0.4),
maxAnimationCount: Int = 20
) {
self.opacity = opacity
self.offsetX = offsetX
self.offsetY = offsetY
self.rotation = rotation
self.scale = scale
self.blur = blur
self.delay = delay
self.animation = animation
self.maxAnimationCount = maxAnimationCount
}
}
let preset: AnimationConfig = AnimationConfig(
opacity: .init(from: 0, to: 1),
offsetX: .init(from: 50, to: 0),
offsetY: .init(from: 10, to: 0),
rotation: .init(from: 0, to: 0),
scale: .init(from: 1.3, to: 1),
blur: .init(from: 8, to: 0)
)
AnimatedForEach(emojiData, preset: preset) { emoji in
}
Variable | Type | Description |
---|---|---|
opacity |
AnimationValue<CGFloat> |
Defines the animation values for opacity. |
offsetX |
AnimationValue<CGFloat> |
Defines the animation values for horizontal offset. |
offsetY |
AnimationValue<CGFloat> |
Defines the animation values for vertical offset. |
rotation |
AnimationValue<Double> |
Defines the animation values for rotation. |
scale |
AnimationValue<Double> |
Defines the animation values for scaling. |
blur |
AnimationValue<Double> |
Defines the animation values for blur effect. |
delay |
Double |
Determines the delay before each animation starts. |
animation |
Animation |
Set to your desired animation type. |
maxAnimationCount |
Int |
Maximum number of animations to play. |
If set to 20, animations play up to the 20th item (index 19) according to the delay. | ||
The 21st and subsequent items will animate simultaneously with the 21st. | ||
This prevents excessive delays when animating large sets of data. | ||
For example, without this option, you would need to wait delay * 99 seconds to see the 100th item. |
var delay: Double = 0.05,
var animation: Animation = .bouncy(duration: 0.4),
var maxAnimationCount: Int = 20
extension AnimationStyle {
var config: AnimationConfig {
switch self {
case .`default`:
return AnimationConfig(
opacity: .init(from: 0, to: 1),
offsetX: .init(from: 0, to: 0),
offsetY: .init(from: 50, to: 0),
rotation: .init(from: 0, to: 0),
scale: .init(from: 1.0, to: 1.0),
blur: .init(from: 0, to: 0)
)
case .grid:
return AnimationConfig(
opacity: .init(from: 0, to: 1),
offsetX: .init(from: 0, to: 0),
offsetY: .init(from: 100, to: 0),
rotation: .init(from: 0, to: 0),
scale: .init(from: 1.3, to: 1.0),
blur: .init(from: 8, to: 0)
)
case .list:
return AnimationConfig(
opacity: .init(from: 0, to: 1),
offsetX: .init(from: 50, to: 0),
offsetY: .init(from: 0, to: 0),
rotation: .init(from: 0, to: 0),
scale: .init(from: 1.1, to: 1.0),
blur: .init(from: 0, to: 0)
)
}
}
}