Skip to content

Commit 2e798b7

Browse files
authored
Allow nil animation (#42)
Additionally: - Tidy up the demo's settings UI by removing outdated setting screen footers. - Improve the demo's settings UX by dynamically displaying pickers relevant only to the currently selected animation.
1 parent 7327e94 commit 2e798b7

File tree

6 files changed

+177
-83
lines changed

6 files changed

+177
-83
lines changed

Demo/Demo.xcodeproj/project.pbxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@
167167
Base,
168168
);
169169
mainGroup = D5535812290E9691009E5D72;
170+
packageReferences = (
171+
);
170172
productRefGroup = D553581C290E9691009E5D72 /* Products */;
171173
projectDirPath = "";
172174
projectRoot = "";

Demo/Demo/AppState.swift

Lines changed: 119 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -70,63 +70,128 @@ final class AppState: ObservableObject {
7070
}
7171
}
7272

73-
struct Animation {
74-
enum Curve: CaseIterable, CustomStringConvertible, Hashable {
75-
case linear
76-
case easeInOut
77-
case spring
78-
79-
var description: String {
80-
switch self {
81-
case .linear:
82-
return "Linear"
83-
case .easeInOut:
84-
return "Ease In Out"
85-
case .spring:
86-
return "Spring"
87-
}
88-
}
89-
}
90-
91-
enum Duration: CaseIterable, CustomStringConvertible, Hashable {
92-
case slow
93-
case medium
94-
case fast
95-
96-
var description: String {
97-
switch self {
98-
case .slow:
99-
return "Slow"
100-
case .medium:
101-
return "Medium"
102-
case .fast:
103-
return "Fast"
104-
}
105-
}
73+
enum Animation: CaseIterable, CustomStringConvertible, Hashable {
74+
case none
75+
case linear
76+
case easeInOut
77+
case spring
10678

107-
func callAsFunction() -> Double {
108-
switch self {
109-
case .slow:
110-
return 1
111-
case .medium:
112-
return 0.6
113-
case .fast:
114-
return 0.35
115-
}
79+
var description: String {
80+
switch self {
81+
case .none:
82+
return "None"
83+
case .linear:
84+
return "Linear"
85+
case .easeInOut:
86+
return "Ease In Out"
87+
case .spring:
88+
return "Spring"
11689
}
11790
}
11891

119-
var curve: Curve
120-
var duration: Duration
121-
122-
func callAsFunction() -> AnyNavigationTransition.Animation {
123-
switch curve {
92+
func callAsFunction(
93+
duration: Duration,
94+
stiffness: Stiffness,
95+
damping: Damping
96+
) -> AnyNavigationTransition.Animation? {
97+
switch self {
98+
case .none:
99+
return .none
124100
case .linear:
125101
return .linear(duration: duration())
126102
case .easeInOut:
127103
return .easeInOut(duration: duration())
128104
case .spring:
129-
return .interpolatingSpring(stiffness: 120, damping: 50)
105+
return .interpolatingSpring(stiffness: stiffness(), damping: damping())
106+
}
107+
}
108+
}
109+
110+
enum Duration: CaseIterable, CustomStringConvertible, Hashable {
111+
case slow
112+
case medium
113+
case fast
114+
115+
var description: String {
116+
switch self {
117+
case .slow:
118+
return "Slow"
119+
case .medium:
120+
return "Medium"
121+
case .fast:
122+
return "Fast"
123+
}
124+
}
125+
126+
func callAsFunction() -> Double {
127+
switch self {
128+
case .slow:
129+
return 1
130+
case .medium:
131+
return 0.6
132+
case .fast:
133+
return 0.35
134+
}
135+
}
136+
}
137+
138+
enum Stiffness: CaseIterable, CustomStringConvertible, Hashable {
139+
case low
140+
case medium
141+
case high
142+
143+
var description: String {
144+
switch self {
145+
case .low:
146+
return "Low"
147+
case .medium:
148+
return "Medium"
149+
case .high:
150+
return "High"
151+
}
152+
}
153+
154+
func callAsFunction() -> Double {
155+
switch self {
156+
case .low:
157+
return 300
158+
case .medium:
159+
return 120
160+
case .high:
161+
return 50
162+
}
163+
}
164+
}
165+
166+
enum Damping: CaseIterable, CustomStringConvertible, Hashable {
167+
case low
168+
case medium
169+
case high
170+
case veryHigh
171+
172+
var description: String {
173+
switch self {
174+
case .low:
175+
return "Low"
176+
case .medium:
177+
return "Medium"
178+
case .high:
179+
return "High"
180+
case .veryHigh:
181+
return "Very High"
182+
}
183+
}
184+
185+
func callAsFunction() -> Double {
186+
switch self {
187+
case .low:
188+
return 20
189+
case .medium:
190+
return 25
191+
case .high:
192+
return 30
193+
case .veryHigh:
194+
return 50
130195
}
131196
}
132197
}
@@ -160,7 +225,12 @@ final class AppState: ObservableObject {
160225
}
161226

162227
@Published var transition: Transition = .slide
163-
@Published var animation: Animation = .init(curve: .easeInOut, duration: .fast)
228+
229+
@Published var animation: Animation = .spring
230+
@Published var duration: Duration = .fast
231+
@Published var stiffness: Stiffness = .low
232+
@Published var damping: Damping = .high
233+
164234
@Published var interactivity: Interactivity = .edgePan
165235

166236
@Published var isPresentingSettings: Bool = false

Demo/Demo/RootView.swift

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,25 @@ struct RootView: View {
1717
.navigationViewStyle(.stack)
1818
}
1919
}
20-
.navigationTransition(
21-
appState.transition().animation(appState.animation()),
22-
interactivity: appState.interactivity()
23-
)
20+
.navigationTransition(transition.animation(animation), interactivity: interactivity)
2421
.sheet(isPresented: $appState.isPresentingSettings) {
2522
SettingsView().environmentObject(appState)
2623
}
2724
}
25+
26+
var transition: AnyNavigationTransition {
27+
appState.transition()
28+
}
29+
30+
var animation: AnyNavigationTransition.Animation? {
31+
appState.animation(
32+
duration: appState.duration,
33+
stiffness: appState.stiffness,
34+
damping: appState.damping
35+
)
36+
}
37+
38+
var interactivity: AnyNavigationTransition.Interactivity {
39+
appState.interactivity()
40+
}
2841
}

Demo/Demo/SettingsView.swift

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,21 @@ struct SettingsView: View {
77
var body: some View {
88
NavigationView {
99
Form {
10-
Section(header: Text("Transition"), footer: transitionFooter) {
10+
Section(header: Text("Transition")) {
1111
picker("Transition", $appState.transition)
1212
}
1313

14-
Section(header: Text("Animation"), footer: animationFooter) {
15-
picker("Curve", $appState.animation.curve)
16-
picker("Duration", $appState.animation.duration)
14+
Section(header: Text("Animation")) {
15+
picker("Animation", $appState.animation)
16+
switch appState.animation {
17+
case .none:
18+
EmptyView()
19+
case .linear, .easeInOut:
20+
picker("Duration", $appState.duration)
21+
case .spring:
22+
picker("Stiffness", $appState.stiffness)
23+
picker("Damping", $appState.damping)
24+
}
1725
}
1826

1927
Section(header: Text("Interactivity"), footer: interactivityFooter) {
@@ -31,22 +39,6 @@ struct SettingsView: View {
3139
.navigationViewStyle(.stack)
3240
}
3341

34-
var transitionFooter: some View {
35-
Text(
36-
"""
37-
"Swing" is a custom transition exclusive to this demo (only 12 lines of code!).
38-
"""
39-
)
40-
}
41-
42-
var animationFooter: some View {
43-
Text(
44-
"""
45-
Note: Duration is ignored when the Spring curve is selected.
46-
"""
47-
)
48-
}
49-
5042
var interactivityFooter: some View {
5143
Text(
5244
"""
@@ -76,8 +68,12 @@ struct SettingsView: View {
7668

7769
func shuffle() {
7870
appState.transition = .allCases.randomElement()!
79-
appState.animation.curve = .allCases.randomElement()!
80-
appState.animation.duration = .allCases.randomElement()!
71+
72+
appState.animation = .allCases.randomElement()!
73+
appState.duration = .allCases.randomElement()!
74+
appState.stiffness = .allCases.randomElement()!
75+
appState.damping = .allCases.randomElement()!
76+
8177
appState.interactivity = .allCases.randomElement()!
8278
}
8379

Sources/NavigationTransition/AnyNavigationTransition.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public struct AnyNavigationTransition {
2222

2323
@_spi(package)public let isDefault: Bool
2424
@_spi(package)public let handler: Handler
25-
@_spi(package)public var animation: Animation = .default
25+
@_spi(package)public var animation: Animation? = .default
2626

2727
public init<T: NavigationTransition>(_ transition: T) {
2828
self.isDefault = false
@@ -42,7 +42,7 @@ extension AnyNavigationTransition {
4242
public typealias Animation = _Animation
4343

4444
/// Attaches an animation to this transition.
45-
public func animation(_ animation: Animation) -> Self {
45+
public func animation(_ animation: Animation?) -> Self {
4646
var copy = self
4747
copy.animation = animation
4848
return copy

Sources/NavigationTransitions/NavigationTransitionDelegate.swift

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,40 @@ import UIKit
55

66
final class NavigationTransitionDelegate: NSObject, UINavigationControllerDelegate {
77
let transition: AnyNavigationTransition
8-
weak var baseDelegate: UINavigationControllerDelegate?
8+
private weak var baseDelegate: UINavigationControllerDelegate?
99
var interactionController: UIPercentDrivenInteractiveTransition?
10+
private var initialAreAnimationsEnabled = UIView.areAnimationsEnabled
1011

1112
init(transition: AnyNavigationTransition, baseDelegate: UINavigationControllerDelegate?) {
1213
self.transition = transition
1314
self.baseDelegate = baseDelegate
1415
}
1516

1617
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
18+
initialAreAnimationsEnabled = UIView.areAnimationsEnabled
19+
UIView.setAnimationsEnabled(transition.animation != nil)
1720
baseDelegate?.navigationController?(navigationController, willShow: viewController, animated: animated)
1821
}
1922

2023
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
2124
baseDelegate?.navigationController?(navigationController, didShow: viewController, animated: animated)
25+
UIView.setAnimationsEnabled(initialAreAnimationsEnabled)
2226
}
2327

2428
func navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
2529
return interactionController
2630
}
2731

2832
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
29-
if let operation = NavigationTransitionOperation(operation) {
30-
return NavigationTransitionAnimatorProvider(transition: transition, operation: operation)
33+
if
34+
let animation = transition.animation,
35+
let operation = NavigationTransitionOperation(operation)
36+
{
37+
return NavigationTransitionAnimatorProvider(
38+
transition: transition,
39+
animation: animation,
40+
operation: operation
41+
)
3142
} else {
3243
return nil
3344
}
@@ -36,15 +47,17 @@ final class NavigationTransitionDelegate: NSObject, UINavigationControllerDelega
3647

3748
final class NavigationTransitionAnimatorProvider: NSObject, UIViewControllerAnimatedTransitioning {
3849
let transition: AnyNavigationTransition
50+
let animation: Animation
3951
let operation: NavigationTransitionOperation
4052

41-
init(transition: AnyNavigationTransition, operation: NavigationTransitionOperation) {
53+
init(transition: AnyNavigationTransition, animation: Animation, operation: NavigationTransitionOperation) {
4254
self.transition = transition
55+
self.animation = animation
4356
self.operation = operation
4457
}
4558

4659
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
47-
transition.animation.duration
60+
animation.duration
4861
}
4962

5063
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
@@ -67,7 +80,7 @@ final class NavigationTransitionAnimatorProvider: NSObject, UIViewControllerAnim
6780
}
6881
let animator = UIViewPropertyAnimator(
6982
duration: transitionDuration(using: transitionContext),
70-
timingParameters: transition.animation.timingParameters
83+
timingParameters: animation.timingParameters
7184
)
7285
cachedAnimators[ObjectIdentifier(transitionContext)] = animator
7386

0 commit comments

Comments
 (0)