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.

179 lines
6.2 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. #if os(iOS)
  24. public class HeroDebugPlugin: HeroPlugin {
  25. public static var showOnTop: Bool = false
  26. var debugView: HeroDebugView?
  27. var zPositionMap = [UIView: CGFloat]()
  28. var addedLayers: [CALayer] = []
  29. var updating = false
  30. override public func animate(fromViews: [UIView], toViews: [UIView]) -> TimeInterval {
  31. if hero.forceNotInteractive { return 0 }
  32. var hasArc = false
  33. for v in context.fromViews + context.toViews where context[v]?.arc != nil && context[v]?.position != nil {
  34. hasArc = true
  35. break
  36. }
  37. let debugView = HeroDebugView(initialProcess: hero.isPresenting ? 0.0 : 1.0, showCurveButton: hasArc, showOnTop: HeroDebugPlugin.showOnTop)
  38. debugView.frame = hero.container.bounds
  39. debugView.delegate = self
  40. hero.container.window?.addSubview(debugView)
  41. debugView.layoutSubviews()
  42. self.debugView = debugView
  43. UIView.animate(withDuration: 0.4) {
  44. debugView.showControls = true
  45. }
  46. return .infinity
  47. }
  48. public override func resume(timePassed: TimeInterval, reverse: Bool) -> TimeInterval {
  49. guard let debugView = debugView else { return 0.4 }
  50. debugView.delegate = nil
  51. UIView.animate(withDuration: 0.4) {
  52. debugView.showControls = false
  53. debugView.debugSlider.setValue(roundf(debugView.progress), animated: true)
  54. }
  55. on3D(wants3D: false)
  56. return 0.4
  57. }
  58. public override func clean() {
  59. debugView?.removeFromSuperview()
  60. debugView = nil
  61. }
  62. }
  63. extension HeroDebugPlugin: HeroDebugViewDelegate {
  64. public func onDone() {
  65. guard let debugView = debugView else { return }
  66. let seekValue = hero.isPresenting ? debugView.progress : 1.0 - debugView.progress
  67. if seekValue > 0.5 {
  68. hero.finish()
  69. } else {
  70. hero.cancel()
  71. }
  72. }
  73. public func onProcessSliderChanged(progress: Float) {
  74. let seekValue = hero.isPresenting ? progress : 1.0 - progress
  75. hero.update(CGFloat(seekValue))
  76. }
  77. func onPerspectiveChanged(translation: CGPoint, rotation: CGFloat, scale: CGFloat) {
  78. var t = CATransform3DIdentity
  79. t.m34 = -1 / 4000
  80. t = CATransform3DTranslate(t, translation.x, translation.y, 0)
  81. t = CATransform3DScale(t, scale, scale, 1)
  82. t = CATransform3DRotate(t, rotation, 0, 1, 0)
  83. hero.container.layer.sublayerTransform = t
  84. }
  85. func animateZPosition(view: UIView, to: CGFloat) {
  86. let a = CABasicAnimation(keyPath: "zPosition")
  87. a.fromValue = view.layer.value(forKeyPath: "zPosition")
  88. a.toValue = NSNumber(value: Double(to))
  89. a.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
  90. a.duration = 0.4
  91. view.layer.add(a, forKey: "zPosition")
  92. view.layer.zPosition = to
  93. }
  94. func onDisplayArcCurve(wantsCurve: Bool) {
  95. for layer in addedLayers {
  96. layer.removeFromSuperlayer()
  97. addedLayers.removeAll()
  98. }
  99. if wantsCurve {
  100. for layer in hero.container.layer.sublayers! {
  101. for (_, anim) in layer.animations {
  102. if let keyframeAnim = anim as? CAKeyframeAnimation, let path = keyframeAnim.path {
  103. let s = CAShapeLayer()
  104. s.zPosition = layer.zPosition + 10
  105. s.path = path
  106. s.strokeColor = UIColor.blue.cgColor
  107. s.fillColor = UIColor.clear.cgColor
  108. hero.container.layer.addSublayer(s)
  109. addedLayers.append(s)
  110. }
  111. }
  112. }
  113. }
  114. }
  115. func on3D(wants3D: Bool) {
  116. var t = CATransform3DIdentity
  117. if wants3D {
  118. var viewsWithZPosition = Set<UIView>()
  119. for view in hero.container.subviews where view.layer.zPosition != 0 {
  120. viewsWithZPosition.insert(view)
  121. zPositionMap[view] = view.layer.zPosition
  122. }
  123. let viewsWithoutZPosition = hero.container.subviews.filter { return !viewsWithZPosition.contains($0) }
  124. let viewsWithPositiveZPosition = viewsWithZPosition.filter { return $0.layer.zPosition > 0 }
  125. for (i, v) in viewsWithoutZPosition.enumerated() {
  126. animateZPosition(view: v, to: CGFloat(i * 10))
  127. }
  128. var maxZPosition: CGFloat = 0
  129. for v in viewsWithPositiveZPosition {
  130. maxZPosition = max(maxZPosition, v.layer.zPosition)
  131. animateZPosition(view: v, to: v.layer.zPosition + CGFloat(viewsWithoutZPosition.count * 10))
  132. }
  133. t.m34 = -1 / 4000
  134. t = CATransform3DTranslate(t, debugView!.translation.x, debugView!.translation.y, 0)
  135. t = CATransform3DScale(t, debugView!.scale, debugView!.scale, 1)
  136. t = CATransform3DRotate(t, debugView!.rotation, 0, 1, 0)
  137. } else {
  138. for v in hero.container.subviews {
  139. animateZPosition(view: v, to: self.zPositionMap[v] ?? 0)
  140. }
  141. self.zPositionMap.removeAll()
  142. }
  143. let a = CABasicAnimation(keyPath: "sublayerTransform")
  144. a.fromValue = hero.container.layer.value(forKeyPath: "sublayerTransform")
  145. a.toValue = NSValue(caTransform3D: t)
  146. a.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
  147. a.duration = 0.4
  148. UIView.animate(withDuration: 0.4) {
  149. self.context.container.backgroundColor = UIColor(white: 0.85, alpha: 1.0)
  150. }
  151. hero.container.layer.add(a, forKey: "debug")
  152. hero.container.layer.sublayerTransform = t
  153. }
  154. }
  155. #endif