Skip to content

Commit a7cc4ec

Browse files
Align .speed modifier for Cocoa animations with SwiftUI (#278)
* Fix .speed modifier for CocoaAnimations - Align with SwiftUI animations - Add issue reporting * Refactor AppKitAnimation extensions and structure Refactor AppKitAnimation by removing unnecessary closing brace and adjusting extensions. * Clarify warning for zero animation speed Updated warning message for animation speed adjustment. * Fix warning message for zero animation speed * Set default speed to 1 in AppKitAnimation.swift --------- Co-authored-by: Stephen Celis <[email protected]> Co-authored-by: Stephen Celis <[email protected]>
1 parent bc0f48d commit a7cc4ec

File tree

2 files changed

+50
-8
lines changed

2 files changed

+50
-8
lines changed

Sources/AppKitNavigation/AppKitAnimation.swift

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
22
import AppKit
33
import SwiftNavigation
4+
import IssueReporting
45

56
#if canImport(SwiftUI)
67
import SwiftUI
@@ -43,7 +44,7 @@
4344
var result: Swift.Result<Result, Error>?
4445
NSAnimationContext.runAnimationGroup { context in
4546
context.allowsImplicitAnimation = true
46-
context.duration = animation.duration
47+
context.duration = animation.duration / animation.speed
4748
context.timingFunction = animation.timingFunction
4849
result = Swift.Result(catching: body)
4950
} completionHandler: {
@@ -74,13 +75,42 @@
7475

7576
fileprivate struct AppKit: Hashable, @unchecked Sendable {
7677
fileprivate var duration: TimeInterval
78+
fileprivate var speed: TimeInterval = 1
7779
fileprivate var timingFunction: CAMediaTimingFunction?
7880

7981
func hash(into hasher: inout Hasher) {
8082
hasher.combine(duration)
8183
}
8284
}
8385
}
86+
87+
/// Changes the duration of an animation by adjusting its speed.
88+
///
89+
/// - Parameter speed: The speed at which SwiftUI performs the animation.
90+
/// - Returns: An animation with the adjusted speed.
91+
public func speed(
92+
_ speed: Double
93+
) -> Self {
94+
switch framework {
95+
case let .swiftUI(animation):
96+
return AppKitAnimation(
97+
framework: .swiftUI(animation.speed(speed))
98+
)
99+
case var .appKit(animation):
100+
if speed != 0 {
101+
animation.speed = speed
102+
} else {
103+
reportIssue(
104+
"""
105+
Setting animation speed to zero is not supported for AppKit animations. \
106+
Replace with '.ulpOfOne' to avoid division by zero.
107+
"""
108+
)
109+
animation.speed = .ulpOfOne
110+
}
111+
return AppKitAnimation(framework: .appKit(animation))
112+
}
113+
}
84114
}
85115

86116
extension AppKitAnimation {

Sources/UIKitNavigation/UIKitAnimation.swift

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#if canImport(UIKit) && !os(watchOS)
22
import UIKit
3+
import IssueReporting
34

45
#if canImport(SwiftUI)
56
import SwiftUI
@@ -71,8 +72,8 @@
7172
var result: Swift.Result<Result, Error>?
7273
withoutActuallyEscaping(animations) { animations in
7374
UIView.animate(
74-
withDuration: animation.duration * animation.speed,
75-
delay: animation.delay * animation.speed,
75+
withDuration: animation.duration / animation.speed,
76+
delay: animation.delay / animation.speed,
7677
options: animation.options,
7778
animations: { result = Swift.Result(catching: animations) },
7879
completion: completion
@@ -84,8 +85,8 @@
8485
var result: Swift.Result<Result, Error>?
8586
withoutActuallyEscaping(animations) { animations in
8687
UIView.animate(
87-
withDuration: animation.duration * animation.speed,
88-
delay: animation.delay * animation.speed,
88+
withDuration: animation.duration / animation.speed,
89+
delay: animation.delay / animation.speed,
8990
usingSpringWithDamping: dampingRatio,
9091
initialSpringVelocity: velocity,
9192
options: animation.options,
@@ -99,10 +100,10 @@
99100
if #available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) {
100101
var result: Swift.Result<Result, Error>?
101102
UIView.animate(
102-
springDuration: animation.duration * animation.speed,
103+
springDuration: animation.duration / animation.speed,
103104
bounce: bounce,
104105
initialSpringVelocity: initialSpringVelocity,
105-
delay: animation.delay * animation.speed,
106+
delay: animation.delay / animation.speed,
106107
options: animation.options,
107108
animations: { result = Swift.Result(catching: animations) },
108109
completion: completion
@@ -220,7 +221,18 @@
220221
framework: .swiftUI(animation.speed(speed))
221222
)
222223
case var .uiKit(animation):
223-
animation.speed = speed
224+
if speed != 0 {
225+
animation.speed = speed
226+
} else {
227+
reportIssue(
228+
"""
229+
Setting animation speed to zero is not supported for UIKit animations.
230+
231+
Replace with '.ulpOfOne' to avoid division by zero.
232+
"""
233+
)
234+
animation.speed = .ulpOfOne
235+
}
224236
return UIKitAnimation(framework: .uiKit(animation))
225237
}
226238
}

0 commit comments

Comments
 (0)