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.

164 lines
5.4 KiB

  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. extension HeroTransition {
  24. open func start() {
  25. guard state == .notified else { return }
  26. state = .starting
  27. if let toView = toView, let fromView = fromView {
  28. if let toViewController = toViewController, let transitionContext = transitionContext {
  29. toView.frame = transitionContext.finalFrame(for: toViewController)
  30. } else {
  31. toView.frame = fromView.frame
  32. }
  33. toView.setNeedsLayout()
  34. toView.layoutIfNeeded()
  35. }
  36. if let fvc = fromViewController, let tvc = toViewController {
  37. closureProcessForHeroDelegate(vc: fvc) {
  38. $0.heroWillStartTransition?()
  39. $0.heroWillStartAnimatingTo?(viewController: tvc)
  40. }
  41. closureProcessForHeroDelegate(vc: tvc) {
  42. $0.heroWillStartTransition?()
  43. $0.heroWillStartAnimatingFrom?(viewController: fvc)
  44. }
  45. }
  46. // take a snapshot to hide all the flashing that might happen
  47. fullScreenSnapshot = transitionContainer?.window?.snapshotView(afterScreenUpdates: false) ?? fromView?.snapshotView(afterScreenUpdates: false)
  48. if let fullScreenSnapshot = fullScreenSnapshot {
  49. (transitionContainer?.window ?? transitionContainer)?.addSubview(fullScreenSnapshot)
  50. }
  51. if let oldSnapshot = fromViewController?.hero.storedSnapshot {
  52. oldSnapshot.removeFromSuperview()
  53. fromViewController?.hero.storedSnapshot = nil
  54. }
  55. if let oldSnapshot = toViewController?.hero.storedSnapshot {
  56. oldSnapshot.removeFromSuperview()
  57. toViewController?.hero.storedSnapshot = nil
  58. }
  59. plugins = HeroTransition.enabledPlugins.map({ return $0.init() })
  60. processors = [
  61. IgnoreSubviewModifiersPreprocessor(),
  62. ConditionalPreprocessor(),
  63. DefaultAnimationPreprocessor(),
  64. MatchPreprocessor(),
  65. SourcePreprocessor(),
  66. CascadePreprocessor()
  67. ]
  68. animators = [
  69. HeroDefaultAnimator<HeroCoreAnimationViewContext>()
  70. ]
  71. if #available(iOS 10, tvOS 10, *) {
  72. animators.append(HeroDefaultAnimator<HeroViewPropertyViewContext>())
  73. }
  74. // There is no covariant in Swift, so we need to add plugins one by one.
  75. for plugin in plugins {
  76. processors.append(plugin)
  77. animators.append(plugin)
  78. }
  79. transitionContainer?.isUserInteractionEnabled = isUserInteractionEnabled
  80. // a view to hold all the animating views
  81. container = UIView(frame: transitionContainer?.bounds ?? .zero)
  82. if !toOverFullScreen && !fromOverFullScreen {
  83. container.backgroundColor = containerColor
  84. }
  85. transitionContainer?.addSubview(container)
  86. context = HeroContext(container: container)
  87. for processor in processors {
  88. processor.hero = self
  89. }
  90. for animator in animators {
  91. animator.hero = self
  92. }
  93. if let toView = toView, let fromView = fromView {
  94. context.loadViewAlpha(rootView: toView)
  95. context.loadViewAlpha(rootView: fromView)
  96. container.addSubview(toView)
  97. container.addSubview(fromView)
  98. toView.updateConstraints()
  99. toView.setNeedsLayout()
  100. toView.layoutIfNeeded()
  101. context.set(fromViews: fromView.flattenedViewHierarchy, toViews: toView.flattenedViewHierarchy)
  102. }
  103. if (viewOrderingStrategy == .auto && !isPresenting && !inTabBarController) ||
  104. viewOrderingStrategy == .sourceViewOnTop {
  105. context.insertToViewFirst = true
  106. }
  107. for processor in processors {
  108. processor.process(fromViews: context.fromViews, toViews: context.toViews)
  109. }
  110. animatingFromViews = context.fromViews.filter { (view: UIView) -> Bool in
  111. for animator in animators {
  112. if animator.canAnimate(view: view, appearing: false) {
  113. return true
  114. }
  115. }
  116. return false
  117. }
  118. animatingToViews = context.toViews.filter { (view: UIView) -> Bool in
  119. for animator in animators {
  120. if animator.canAnimate(view: view, appearing: true) {
  121. return true
  122. }
  123. }
  124. return false
  125. }
  126. if let toView = toView {
  127. context.hide(view: toView)
  128. }
  129. #if os(tvOS)
  130. animate()
  131. #else
  132. if inNavigationController {
  133. // When animating within navigationController, we have to dispatch later into the main queue.
  134. // otherwise snapshots will be pure white. Possibly a bug with UIKit
  135. DispatchQueue.main.async {
  136. self.animate()
  137. }
  138. } else {
  139. animate()
  140. }
  141. #endif
  142. }
  143. }