Advertisement
Guest User

Untitled

a guest
Dec 10th, 2016
68
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.69 KB | None | 0 0
  1. //
  2. // JTSSwiftTweener.swift
  3. // JTSSwiftTweener
  4. //
  5. // Created by Joshua Sullivan on 12/10/16.
  6. // Copyright © 2016 Josh Sullivan. All rights reserved.
  7. //
  8.  
  9. import UIKit
  10.  
  11. /// The `Tweener` class allows for the easy animation of arbitrary numeric values,
  12. /// including those that are not associated with views.
  13. public final class Tweener: Equatable {
  14.  
  15. /// A closure invoked when the tween updates.
  16. /// The `Double` parameter is the current value of the tween. The `CFTimeInterval` parameter is the elapsed time of the tween.
  17. /// - Note: The `TweenProgress` closure is gauranteed to be called with the `from` value first and the `to` value of the tween immediately prior to the `TweenComplete` closure being called.
  18. public typealias TweenProgress = (Double, CFTimeInterval) -> Void
  19.  
  20. /// A closure invoked when the tween completes or is canceled.
  21. /// - The `Bool` value will be `true` if the the tween completed and `false` if it was canceled.
  22. public typealias TweenComplete = (Bool) -> Void
  23.  
  24. /// The currently active tweens.
  25. fileprivate static var tweens: [Tweener] = [] {
  26. didSet {
  27. if tweens.isEmpty {
  28. pause()
  29. } else {
  30. resume()
  31. }
  32. }
  33. }
  34.  
  35. /// The display link that drives the animation engine.
  36. fileprivate static let displayLink: CADisplayLink = {
  37. let dl = CADisplayLink(target: Tweener.self, selector: #selector(tick(link:)))
  38. dl.add(to: .main, forMode: .commonModes)
  39. dl.isPaused = true
  40. return dl
  41. }()
  42.  
  43. /// Used to generate unique ids for the individual `Tweener` instances.
  44. fileprivate static var idCounter: Int = 0
  45.  
  46. /// The last update of the tween, used to determine the elapsed time between ticks.
  47. fileprivate static var previousTimestamp: CFTimeInterval = 0.0
  48.  
  49. /// Handle the display link tick.
  50. @objc fileprivate static func tick(link: CADisplayLink) {
  51. let dt = link.timestamp - previousTimestamp
  52. let tweensDidFinish = tweens.reduce(false, { return $0 || $1.tick(elapsedTime: dt) })
  53. if tweensDidFinish {
  54. tweens = tweens.filter({ !$0.isComplete })
  55. }
  56. previousTimestamp = link.timestamp
  57. }
  58.  
  59. /// Start sending updates to the Tweener instances.
  60. fileprivate static func resume() {
  61. previousTimestamp = CACurrentMediaTime()
  62. displayLink.isPaused = false
  63. }
  64.  
  65. /// Stop sending updates to Tweener instances.
  66. fileprivate static func pause() {
  67. displayLink.isPaused = true
  68. }
  69.  
  70.  
  71. /// Create a Tweener instance with the specified parameters.
  72. /// - Warning: If you keep a strong reference to the returned tweener object you could create
  73. /// a retain cycle unless you're careful to only use `weak` or `unowned` self in the
  74. /// progress and completion closures.
  75. ///
  76. /// - Parameters:
  77. /// - duration: The duration of the tween, in seconds.
  78. /// - from: The starting value of the tween.
  79. /// - to: The ending value of the tween.
  80. /// - progress: The closure to be invoked when the tween updates.
  81. /// - completion: The optional closure to be invoked when the tween completes.
  82. /// - Returns: The tweener object which is useful for pausing or canceling the tween.
  83. @discardableResult public static func tween(duration: CFTimeInterval, from: Double = 0.0, to: Double = 1.0, easing: @escaping TweenerEasing.EasingTransform = TweenerEasing.Quadratic.easeInOut, progress: @escaping TweenProgress, completion: TweenComplete? = nil) -> Tweener {
  84. let tweener = Tweener(id: idCounter, duration: duration, from: from, to: to, easing: easing, progress: progress, completion: completion)
  85. tweens.append(tweener)
  86. idCounter += 1
  87. return tweener
  88. }
  89.  
  90. // MARK: Instance Variables
  91.  
  92. /// An internal means for uniquely identifying Tweener instance.
  93. fileprivate let id: Int
  94.  
  95. /// The duration of the tween.
  96. fileprivate let duration: CFTimeInterval
  97.  
  98. /// The starting value of the tween.
  99. fileprivate let fromValue: Double
  100.  
  101. /// The ending value of the tween.
  102. fileprivate let toValue: Double
  103.  
  104. /// The easing transform to use.
  105. fileprivate let easing: TweenerEasing.EasingTransform
  106.  
  107. /// The progress closure for the tween.
  108. fileprivate let progress: TweenProgress
  109.  
  110. /// The completion closure for the tween.
  111. fileprivate let completion: TweenComplete?
  112.  
  113. /// The paused state of the tween. Setting this to true will prevent the progress closure from being called.
  114. /// When unpaused, the tween resumes where it left off, regardless of the elapsed time.
  115. public var isPaused: Bool = false
  116.  
  117. /// Set once the tween is completed.
  118. fileprivate var isComplete: Bool = false
  119.  
  120. /// The amount of time that has elapsed since the tween started. This value does not increase while the tween is paused.
  121. fileprivate(set) var elapsedTime: CFTimeInterval = 0.0
  122.  
  123. // MARK: Lifecycle
  124.  
  125. fileprivate init(id: Int, duration: CFTimeInterval, from: Double = 0.0, to: Double = 1.0, easing: @escaping TweenerEasing.EasingTransform, progress: @escaping TweenProgress, completion: TweenComplete? = nil) {
  126. self.id = id
  127. self.duration = duration
  128. self.fromValue = from
  129. self.toValue = to
  130. self.progress = progress
  131. self.easing = easing
  132. self.completion = completion
  133. self.progress(from, 0.0)
  134. }
  135.  
  136. /// Cancel a tween. Calling this method will cause the completion closure to be invoked with a value of false.
  137. public func cancel() {
  138. isComplete = true
  139. completion?(false)
  140. }
  141.  
  142. /// Invoked by the class to advance the tween.
  143. /// - Returns: `true` if the tween finished, otherwise `false`.
  144. fileprivate func tick(elapsedTime dt: CFTimeInterval) -> Bool {
  145. elapsedTime += dt
  146. guard elapsedTime < duration else {
  147. self.progress(toValue, duration)
  148. completion?(true)
  149. isComplete = true
  150. return true
  151. }
  152. let progress = elapsedTime / duration
  153. let eased = easing(progress)
  154. let value = (toValue - fromValue) * eased + fromValue
  155. self.progress(value, elapsedTime)
  156. return false
  157. }
  158. }
  159.  
  160. public func == (t0: Tweener, t1: Tweener) -> Bool { return t0.id == t1.id }
  161.  
  162. public enum TweenerEasing {
  163.  
  164. /// A closure which takes progress scalar (0.0 - 1.0) and transforms it to a different progress scalar.
  165. public typealias EasingTransform = (Double) -> Double
  166.  
  167. /// The `Linear` easing does not ease the progress.
  168. public enum Linear {
  169. public static let easeNone: EasingTransform = {
  170. (progress: Double) -> Double in return progress
  171. }
  172. }
  173.  
  174. /// The `Quadratic` easing is an average easing family, similar to what is provided by CAAnimation.
  175. public enum Quadratic {
  176.  
  177. /// The animation starts slowly and ends more quickly.
  178. public static let easeIn: EasingTransform = {
  179. (progress: Double) -> Double in
  180. return progress * progress
  181. }
  182.  
  183. /// The animation starts quickly and ends more slowly.
  184. public static let easeOut: EasingTransform = {
  185. (progress: Double) -> Double in
  186. return -(progress * (progress - 2.0))
  187. }
  188.  
  189. /// The animation is slow at both ends and quicker in the middle.
  190. public static let easeInOut: EasingTransform = {
  191. (progress: Double) -> Double in
  192. if (progress < 0.5) {
  193. return 2.0 * progress * progress;
  194. }
  195. else {
  196. return (-2.0 * progress * progress) + (4.0 * progress) - 1.0;
  197. }
  198. }
  199. }
  200. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement