1 //  Copyright (c) 2018, Applidium. All rights reserved
2 //  SpringOverlayTranslationAnimationController.swift
3 //  OverlayContainer
4 //
5 //  Created by Gaétan Zanella on 21/11/2018.
6 //
7 
8 import UIKit
9 
10 private struct Constant {
11     static let defaultMass: CGFloat = 1
12     static let defaultDamping: CGFloat = 0.7
13     static let defaultRigidDamping: CGFloat = 0.9
14     static let defaultResponse: CGFloat = 0.3
15     static let minimumDamping: CGFloat = 1
16     static let minimumVelocityConsideration: CGFloat = 150
17     static let maximumVelocityConsideration: CGFloat = 3000
18 }
19 
20 /// An `OverlayAnimatedTransitioning` implementation based on `UISpringTimingParameters`.
21 public class SpringOverlayTranslationAnimationController: OverlayAnimatedTransitioning {
22 
23     public var mass: CGFloat = Constant.defaultMass
24     public var damping: CGFloat = Constant.defaultDamping
25     public var response: CGFloat = Constant.defaultResponse
26 
27     // MARK: - Life Cycle
28 
29     public init() {}
30 
31     public init(style: OverlayContainerViewController.OverlayStyle) {
32         switch style {
33         case .expandableHeight, .rigid:
34             // (gz) 2019-06-15 We also nullify the damping value when using rigid styles
35             // to avoid the panel to be lifted above the bottom of the screen.
36             damping = Constant.defaultRigidDamping
37         case .flexibleHeight:
38             damping = Constant.defaultDamping
39         }
40     }
41 
42     // MARK: - OverlayAnimatedTransitioning
43 
interruptibleAnimatornull44     public func interruptibleAnimator(using context: OverlayContainerContextTransitioning) -> UIViewImplicitlyAnimating {
45         let velocity = min(
46             Constant.maximumVelocityConsideration,
47             max(abs(context.velocity.y), Constant.minimumVelocityConsideration)
48         )
49         let velocityRange = Constant.maximumVelocityConsideration - Constant.minimumVelocityConsideration
50         let normalizedVelocity = (velocity - Constant.minimumVelocityConsideration) / velocityRange
51         let normalizedDamping = normalizedVelocity * (damping - Constant.minimumDamping) + Constant.minimumDamping
52         let timing = UISpringTimingParameters(
53             damping: normalizedDamping,
54             response: response,
55             mass: mass
56         )
57         return UIViewPropertyAnimator(
58             duration: 0, // duration is ignored when using `UISpringTimingParameters.init(mass:stiffness:damping:initialVelocity)`
59             timingParameters: timing
60         )
61     }
62 }
63 
64 extension UISpringTimingParameters {
65     convenience init(damping: CGFloat, response: CGFloat, mass: CGFloat) {
66         let stiffness = pow(2 * .pi / response, 2)
67         let damp = 4 * .pi * damping / response
68         self.init(mass: mass, stiffness: stiffness, damping: damp, initialVelocity: .zero)
69     }
70 }
71