Skip to content

Commit

Permalink
Allow nil animation (#42)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
davdroman authored Dec 7, 2022
1 parent 7327e94 commit 2e798b7
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 83 deletions.
2 changes: 2 additions & 0 deletions Demo/Demo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@
Base,
);
mainGroup = D5535812290E9691009E5D72;
packageReferences = (
);
productRefGroup = D553581C290E9691009E5D72 /* Products */;
projectDirPath = "";
projectRoot = "";
Expand Down
168 changes: 119 additions & 49 deletions Demo/Demo/AppState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,63 +70,128 @@ final class AppState: ObservableObject {
}
}

struct Animation {
enum Curve: CaseIterable, CustomStringConvertible, Hashable {
case linear
case easeInOut
case spring

var description: String {
switch self {
case .linear:
return "Linear"
case .easeInOut:
return "Ease In Out"
case .spring:
return "Spring"
}
}
}

enum Duration: CaseIterable, CustomStringConvertible, Hashable {
case slow
case medium
case fast

var description: String {
switch self {
case .slow:
return "Slow"
case .medium:
return "Medium"
case .fast:
return "Fast"
}
}
enum Animation: CaseIterable, CustomStringConvertible, Hashable {
case none
case linear
case easeInOut
case spring

func callAsFunction() -> Double {
switch self {
case .slow:
return 1
case .medium:
return 0.6
case .fast:
return 0.35
}
var description: String {
switch self {
case .none:
return "None"
case .linear:
return "Linear"
case .easeInOut:
return "Ease In Out"
case .spring:
return "Spring"
}
}

var curve: Curve
var duration: Duration

func callAsFunction() -> AnyNavigationTransition.Animation {
switch curve {
func callAsFunction(
duration: Duration,
stiffness: Stiffness,
damping: Damping
) -> AnyNavigationTransition.Animation? {
switch self {
case .none:
return .none
case .linear:
return .linear(duration: duration())
case .easeInOut:
return .easeInOut(duration: duration())
case .spring:
return .interpolatingSpring(stiffness: 120, damping: 50)
return .interpolatingSpring(stiffness: stiffness(), damping: damping())
}
}
}

enum Duration: CaseIterable, CustomStringConvertible, Hashable {
case slow
case medium
case fast

var description: String {
switch self {
case .slow:
return "Slow"
case .medium:
return "Medium"
case .fast:
return "Fast"
}
}

func callAsFunction() -> Double {
switch self {
case .slow:
return 1
case .medium:
return 0.6
case .fast:
return 0.35
}
}
}

enum Stiffness: CaseIterable, CustomStringConvertible, Hashable {
case low
case medium
case high

var description: String {
switch self {
case .low:
return "Low"
case .medium:
return "Medium"
case .high:
return "High"
}
}

func callAsFunction() -> Double {
switch self {
case .low:
return 300
case .medium:
return 120
case .high:
return 50
}
}
}

enum Damping: CaseIterable, CustomStringConvertible, Hashable {
case low
case medium
case high
case veryHigh

var description: String {
switch self {
case .low:
return "Low"
case .medium:
return "Medium"
case .high:
return "High"
case .veryHigh:
return "Very High"
}
}

func callAsFunction() -> Double {
switch self {
case .low:
return 20
case .medium:
return 25
case .high:
return 30
case .veryHigh:
return 50
}
}
}
Expand Down Expand Up @@ -160,7 +225,12 @@ final class AppState: ObservableObject {
}

@Published var transition: Transition = .slide
@Published var animation: Animation = .init(curve: .easeInOut, duration: .fast)

@Published var animation: Animation = .spring
@Published var duration: Duration = .fast
@Published var stiffness: Stiffness = .low
@Published var damping: Damping = .high

@Published var interactivity: Interactivity = .edgePan

@Published var isPresentingSettings: Bool = false
Expand Down
21 changes: 17 additions & 4 deletions Demo/Demo/RootView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,25 @@ struct RootView: View {
.navigationViewStyle(.stack)
}
}
.navigationTransition(
appState.transition().animation(appState.animation()),
interactivity: appState.interactivity()
)
.navigationTransition(transition.animation(animation), interactivity: interactivity)
.sheet(isPresented: $appState.isPresentingSettings) {
SettingsView().environmentObject(appState)
}
}

var transition: AnyNavigationTransition {
appState.transition()
}

var animation: AnyNavigationTransition.Animation? {
appState.animation(
duration: appState.duration,
stiffness: appState.stiffness,
damping: appState.damping
)
}

var interactivity: AnyNavigationTransition.Interactivity {
appState.interactivity()
}
}
40 changes: 18 additions & 22 deletions Demo/Demo/SettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,21 @@ struct SettingsView: View {
var body: some View {
NavigationView {
Form {
Section(header: Text("Transition"), footer: transitionFooter) {
Section(header: Text("Transition")) {
picker("Transition", $appState.transition)
}

Section(header: Text("Animation"), footer: animationFooter) {
picker("Curve", $appState.animation.curve)
picker("Duration", $appState.animation.duration)
Section(header: Text("Animation")) {
picker("Animation", $appState.animation)
switch appState.animation {
case .none:
EmptyView()
case .linear, .easeInOut:
picker("Duration", $appState.duration)
case .spring:
picker("Stiffness", $appState.stiffness)
picker("Damping", $appState.damping)
}
}

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

var transitionFooter: some View {
Text(
"""
"Swing" is a custom transition exclusive to this demo (only 12 lines of code!).
"""
)
}

var animationFooter: some View {
Text(
"""
Note: Duration is ignored when the Spring curve is selected.
"""
)
}

var interactivityFooter: some View {
Text(
"""
Expand Down Expand Up @@ -76,8 +68,12 @@ struct SettingsView: View {

func shuffle() {
appState.transition = .allCases.randomElement()!
appState.animation.curve = .allCases.randomElement()!
appState.animation.duration = .allCases.randomElement()!

appState.animation = .allCases.randomElement()!
appState.duration = .allCases.randomElement()!
appState.stiffness = .allCases.randomElement()!
appState.damping = .allCases.randomElement()!

appState.interactivity = .allCases.randomElement()!
}

Expand Down
4 changes: 2 additions & 2 deletions Sources/NavigationTransition/AnyNavigationTransition.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public struct AnyNavigationTransition {

@_spi(package)public let isDefault: Bool
@_spi(package)public let handler: Handler
@_spi(package)public var animation: Animation = .default
@_spi(package)public var animation: Animation? = .default

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

/// Attaches an animation to this transition.
public func animation(_ animation: Animation) -> Self {
public func animation(_ animation: Animation?) -> Self {
var copy = self
copy.animation = animation
return copy
Expand Down
25 changes: 19 additions & 6 deletions Sources/NavigationTransitions/NavigationTransitionDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,40 @@ import UIKit

final class NavigationTransitionDelegate: NSObject, UINavigationControllerDelegate {
let transition: AnyNavigationTransition
weak var baseDelegate: UINavigationControllerDelegate?
private weak var baseDelegate: UINavigationControllerDelegate?
var interactionController: UIPercentDrivenInteractiveTransition?
private var initialAreAnimationsEnabled = UIView.areAnimationsEnabled

init(transition: AnyNavigationTransition, baseDelegate: UINavigationControllerDelegate?) {
self.transition = transition
self.baseDelegate = baseDelegate
}

func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
initialAreAnimationsEnabled = UIView.areAnimationsEnabled
UIView.setAnimationsEnabled(transition.animation != nil)
baseDelegate?.navigationController?(navigationController, willShow: viewController, animated: animated)
}

func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
baseDelegate?.navigationController?(navigationController, didShow: viewController, animated: animated)
UIView.setAnimationsEnabled(initialAreAnimationsEnabled)
}

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

func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
if let operation = NavigationTransitionOperation(operation) {
return NavigationTransitionAnimatorProvider(transition: transition, operation: operation)
if
let animation = transition.animation,
let operation = NavigationTransitionOperation(operation)
{
return NavigationTransitionAnimatorProvider(
transition: transition,
animation: animation,
operation: operation
)
} else {
return nil
}
Expand All @@ -36,15 +47,17 @@ final class NavigationTransitionDelegate: NSObject, UINavigationControllerDelega

final class NavigationTransitionAnimatorProvider: NSObject, UIViewControllerAnimatedTransitioning {
let transition: AnyNavigationTransition
let animation: Animation
let operation: NavigationTransitionOperation

init(transition: AnyNavigationTransition, operation: NavigationTransitionOperation) {
init(transition: AnyNavigationTransition, animation: Animation, operation: NavigationTransitionOperation) {
self.transition = transition
self.animation = animation
self.operation = operation
}

func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
transition.animation.duration
animation.duration
}

func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
Expand All @@ -67,7 +80,7 @@ final class NavigationTransitionAnimatorProvider: NSObject, UIViewControllerAnim
}
let animator = UIViewPropertyAnimator(
duration: transitionDuration(using: transitionContext),
timingParameters: transition.animation.timingParameters
timingParameters: animation.timingParameters
)
cachedAnimators[ObjectIdentifier(transitionContext)] = animator

Expand Down

0 comments on commit 2e798b7

Please sign in to comment.