Skip to content

Commit 0f73ee9

Browse files
committed
Add withAnimation and the animation modifier
1 parent 0e7db5c commit 0f73ee9

1 file changed

Lines changed: 78 additions & 0 deletions

File tree

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//
2+
// Animation.swift
3+
// AndroidSwiftUICore
4+
//
5+
// Animation is interpreter-side: Swift only describes intent. `withAnimation`
6+
// records the animation on the transaction; the state writes inside its body
7+
// mark the host's next evaluation, which stamps the root node. The Compose
8+
// interpreter then eases changed modifier values instead of snapping them.
9+
//
10+
11+
/// A description of how value changes should be eased.
12+
public struct Animation: Equatable, Sendable {
13+
14+
internal var curve: String
15+
internal var duration: Double // seconds
16+
17+
public static let `default` = Animation(curve: "easeInOut", duration: 0.35)
18+
19+
public static func linear(duration: Double = 0.35) -> Animation {
20+
Animation(curve: "linear", duration: duration)
21+
}
22+
23+
public static func easeIn(duration: Double = 0.35) -> Animation {
24+
Animation(curve: "easeIn", duration: duration)
25+
}
26+
27+
public static func easeOut(duration: Double = 0.35) -> Animation {
28+
Animation(curve: "easeOut", duration: duration)
29+
}
30+
31+
public static func easeInOut(duration: Double = 0.35) -> Animation {
32+
Animation(curve: "easeInOut", duration: duration)
33+
}
34+
35+
public static func spring() -> Animation {
36+
Animation(curve: "spring", duration: 0.5)
37+
}
38+
}
39+
40+
/// The animation context active while a `withAnimation` body runs. Main-thread
41+
/// confined by the same contract as the rest of the pipeline.
42+
public enum Transaction {
43+
nonisolated(unsafe) public static var _current: Animation?
44+
}
45+
46+
/// Runs `body`, easing the view updates its state writes produce.
47+
public func withAnimation<Result>(
48+
_ animation: Animation = .default,
49+
_ body: () throws -> Result
50+
) rethrows -> Result {
51+
let previous = Transaction._current
52+
Transaction._current = animation
53+
defer { Transaction._current = previous }
54+
return try body()
55+
}
56+
57+
// MARK: - Implicit animation
58+
59+
/// Marks a view so changes to its modifier values ease whenever `value`
60+
/// changes, without an explicit `withAnimation` at the write site.
61+
public struct _AnimationModifier: RenderModifier {
62+
let animation: Animation?
63+
let token: String
64+
public var _modifierNode: ModifierNode {
65+
guard let animation else { return ModifierNode(kind: "animation") }
66+
return ModifierNode(kind: "animation", args: [
67+
"curve": .string(animation.curve),
68+
"durationMs": .double(animation.duration * 1000),
69+
"token": .string(token),
70+
])
71+
}
72+
}
73+
74+
public extension View {
75+
func animation<V: Equatable>(_ animation: Animation?, value: V) -> ModifiedContent<Self, _AnimationModifier> {
76+
modifier(_AnimationModifier(animation: animation, token: String(describing: value)))
77+
}
78+
}

0 commit comments

Comments
 (0)