Skip to content

Commit 4bd1f28

Browse files
authored
Add withAnimation API (#416)
* Add withAnimation support * Add ElasticEaseInEaseOutAnimationExample
1 parent 4ec160b commit 4bd1f28

File tree

4 files changed

+76
-3
lines changed

4 files changed

+76
-3
lines changed

Example/HostingExample/ViewController.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,6 @@ class ViewController: NSViewController {
6666

6767
struct ContentView: View {
6868
var body: some View {
69-
ValueActionModifierExample()
69+
ElasticEaseInEaseOutView()
7070
}
7171
}

Example/SharedExample/Animation/ColorAnimationExample.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import OpenSwiftUI
77
#else
88
import SwiftUI
99
#endif
10-
import Foundation
1110

1211
struct ColorAnimationExample: View {
1312
@State private var showRed = false
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//
2+
// ElasticEaseInEaseOutAnimationExample.swift
3+
// SharedExample
4+
5+
#if OPENSWIFTUI
6+
import OpenSwiftUI
7+
#else
8+
import SwiftUI
9+
#endif
10+
import Foundation
11+
12+
private struct ElasticEaseInEaseOutAnimation: CustomAnimation {
13+
let duration: TimeInterval
14+
15+
func animate<V>(value: V, time: TimeInterval, context: inout AnimationContext<V>) -> V? where V: VectorArithmetic {
16+
if time > duration { return nil } // The animation has finished.
17+
18+
let p = time / duration
19+
let s = sin((20 * p - 11.125) * ((2 * Double.pi) / 4.5))
20+
if p < 0.5 {
21+
return value.scaled(by: -(pow(2, 20 * p - 10) * s) / 2)
22+
} else {
23+
return value.scaled(by: (pow(2, -20 * p + 10) * s) / 2 + 1)
24+
}
25+
}
26+
}
27+
28+
extension Animation {
29+
static var elasticEaseInEaseOut: Animation { elasticEaseInEaseOut(duration: 0.35) }
30+
31+
static func elasticEaseInEaseOut(duration: TimeInterval) -> Animation {
32+
Animation(ElasticEaseInEaseOutAnimation(duration: duration))
33+
}
34+
}
35+
36+
struct ElasticEaseInEaseOutView: View {
37+
@State private var isActive = false
38+
39+
var body: some View {
40+
VStack(alignment: isActive ? .trailing : .leading) {
41+
Color.red
42+
.frame(width: 100.0, height: 100.0)
43+
.onAppear {
44+
withAnimation(.elasticEaseInEaseOut(duration: 5.0)) {
45+
isActive.toggle()
46+
}
47+
}
48+
Color.blue
49+
.frame(maxWidth: .infinity)
50+
}
51+
.padding()
52+
}
53+
}

Sources/OpenSwiftUICore/Animation/Transaction/Transaction.swift

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Transaction.swift
33
// OpenSwiftUICore
44
//
5-
// Audited for iOS 18.0
5+
// Audited for 6.0.87
66
// Status: Complete
77
// ID: B2543BCA257433E04979186A1DC2B6BC (SwiftUICore)
88

@@ -51,6 +51,7 @@ import OpenSwiftUI_SPI
5151
/// transaction.animation = .default.repeatCount(3)
5252
/// }
5353
/// }
54+
@available(OpenSwiftUI_v5_0, *)
5455
public protocol TransactionKey {
5556
/// The associated type representing the type of the transaction key's
5657
/// value.
@@ -62,12 +63,14 @@ public protocol TransactionKey {
6263
static func _valuesEqual(_ lhs: Value, _ rhs: Value) -> Swift.Bool
6364
}
6465

66+
@available(OpenSwiftUI_v5_0, *)
6567
extension TransactionKey {
6668
public static func _valuesEqual(_ lhs: Value, _ rhs: Value) -> Bool {
6769
compareValues(lhs, rhs)
6870
}
6971
}
7072

73+
@available(OpenSwiftUI_v5_0, *)
7174
extension TransactionKey where Value: Equatable {
7275
public static func _valuesEqual(_ lhs: Value, _ rhs: Value) -> Bool {
7376
lhs == rhs
@@ -95,6 +98,7 @@ private struct TransactionPropertyKey<Key>: PropertyKey where Key: TransactionKe
9598
/// The root transaction for a state change comes from the binding that changed,
9699
/// plus any global values set by calling ``withTransaction(_:_:)`` or
97100
/// ``withAnimation(_:_:)``
101+
@available(OpenSwiftUI_v1_0, *)
98102
@frozen
99103
public struct Transaction {
100104
@usableFromInline
@@ -189,6 +193,7 @@ extension Transaction: Sendable {}
189193
///
190194
/// - Returns: The result of executing the closure with the specified
191195
/// transaction.
196+
@available(OpenSwiftUI_v1_0, *)
192197
public func withTransaction<Result>(
193198
_ transaction: Transaction,
194199
_ body: () throws -> Result
@@ -218,6 +223,7 @@ public func withTransaction<Result>(
218223
///
219224
/// - Returns: The result of executing the closure with the specified
220225
/// transaction value.
226+
@available(OpenSwiftUI_v1_0, *)
221227
@_alwaysEmitIntoClient
222228
public func withTransaction<R, V>(
223229
_ keyPath: WritableKeyPath<Transaction, V>,
@@ -240,3 +246,18 @@ private var threadTransactionData: AnyObject? {
240246
)
241247
}
242248
}
249+
250+
/// Returns the result of recomputing the view's body with the provided
251+
/// animation.
252+
///
253+
/// This function sets the given ``Animation`` as the ``Transaction/animation``
254+
/// property of the thread's current ``Transaction``.
255+
@available(OpenSwiftUI_v1_0, *)
256+
public func withAnimation<Result>(
257+
_ animation: Animation? = .default,
258+
_ body: () throws -> Result
259+
) rethrows -> Result {
260+
var transaction = Transaction()
261+
transaction.animation = animation
262+
return try withTransaction(transaction, body)
263+
}

0 commit comments

Comments
 (0)