You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

171 lines
5.9 KiB

6 years ago
  1. // The MIT License (MIT)
  2. //
  3. // Copyright (c) 2016 Luke Zhao <me@lkzhao.com>
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. import UIKit
  23. open class HeroPlugin: NSObject, HeroPreprocessor, HeroAnimator {
  24. weak public var hero: HeroTransition!
  25. public var context: HeroContext! {
  26. return hero.context
  27. }
  28. /**
  29. Determines whether or not to receive `seekTo` callback on every frame.
  30. Default is false.
  31. When **requirePerFrameCallback** is **false**, the plugin needs to start its own animations inside `animate` & `resume`
  32. The `seekTo` method is only being called during an interactive transition.
  33. When **requirePerFrameCallback** is **true**, the plugin will receive `seekTo` callback on every animation frame. Hence it is possible for the plugin to do per-frame animations without implementing `animate` & `resume`
  34. */
  35. open var requirePerFrameCallback = false
  36. public override required init() {}
  37. /**
  38. Called before any animation.
  39. Override this method when you want to preprocess modifiers for views
  40. - Parameters:
  41. - context: object holding all parsed and changed modifiers,
  42. - fromViews: A flattened list of all views from source ViewController
  43. - toViews: A flattened list of all views from destination ViewController
  44. To check a view's modifiers:
  45. context[view]
  46. context[view, "modifierName"]
  47. To set a view's modifiers:
  48. context[view] = [("modifier1", ["parameter1"]), ("modifier2", [])]
  49. context[view, "modifier1"] = ["parameter1", "parameter2"]
  50. */
  51. open func process(fromViews: [UIView], toViews: [UIView]) {}
  52. /**
  53. - Returns: return true if the plugin can handle animating the view.
  54. - Parameters:
  55. - context: object holding all parsed and changed modifiers,
  56. - view: the view to check whether or not the plugin can handle the animation
  57. - appearing: true if the view is appearing(i.e. a view in destination ViewController)
  58. If return true, Hero won't animate and won't let any other plugins animate this view.
  59. The view will also be hidden automatically during the animation.
  60. */
  61. open func canAnimate(view: UIView, appearing: Bool) -> Bool { return false }
  62. /**
  63. Perform the animation.
  64. Note: views in `fromViews` & `toViews` are hidden already. Unhide then if you need to take snapshots.
  65. - Parameters:
  66. - context: object holding all parsed and changed modifiers,
  67. - fromViews: A flattened list of all views from source ViewController (filtered by `canAnimate`)
  68. - toViews: A flattened list of all views from destination ViewController (filtered by `canAnimate`)
  69. - Returns: The duration needed to complete the animation
  70. */
  71. open func animate(fromViews: [UIView], toViews: [UIView]) -> TimeInterval { return 0 }
  72. /**
  73. Called when all animations are completed.
  74. Should perform cleanup and release any reference
  75. */
  76. open func clean() {}
  77. /**
  78. For supporting interactive animation only.
  79. This method is called when an interactive animation is in place
  80. The plugin should pause the animation, and seek to the given progress
  81. - Parameters:
  82. - timePassed: time of the animation to seek to.
  83. */
  84. open func seekTo(timePassed: TimeInterval) {}
  85. /**
  86. For supporting interactive animation only.
  87. This method is called when an interactive animation is ended
  88. The plugin should resume the animation.
  89. - Parameters:
  90. - timePassed: will be the same value since last `seekTo`
  91. - reverse: a boolean value indicating whether or not the animation should reverse
  92. */
  93. open func resume(timePassed: TimeInterval, reverse: Bool) -> TimeInterval { return 0 }
  94. /**
  95. For supporting interactive animation only.
  96. This method is called when user wants to override animation modifiers during an interactive animation
  97. - Parameters:
  98. - state: the target state to override
  99. - view: the view to override
  100. */
  101. open func apply(state: HeroTargetState, to view: UIView) {}
  102. open func changeTarget(state: HeroTargetState, isDestination: Bool, to view: UIView) {}
  103. }
  104. // methods for enable/disable the current plugin
  105. extension HeroPlugin {
  106. public static var isEnabled: Bool {
  107. get {
  108. return HeroTransition.isEnabled(plugin: self)
  109. }
  110. set {
  111. if newValue {
  112. enable()
  113. } else {
  114. disable()
  115. }
  116. }
  117. }
  118. public static func enable() {
  119. HeroTransition.enable(plugin: self)
  120. }
  121. public static func disable() {
  122. HeroTransition.disable(plugin: self)
  123. }
  124. }
  125. // MARK: Plugin Support
  126. internal extension HeroTransition {
  127. static func isEnabled(plugin: HeroPlugin.Type) -> Bool {
  128. return enabledPlugins.index(where: { return $0 == plugin}) != nil
  129. }
  130. static func enable(plugin: HeroPlugin.Type) {
  131. disable(plugin: plugin)
  132. enabledPlugins.append(plugin)
  133. }
  134. static func disable(plugin: HeroPlugin.Type) {
  135. if let index = enabledPlugins.index(where: { return $0 == plugin}) {
  136. enabledPlugins.remove(at: index)
  137. }
  138. }
  139. }