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.

126 lines
4.0 KiB

6 years ago
  1. //
  2. // CascadeEffect.swift
  3. // The MIT License (MIT)
  4. //
  5. // Copyright (c) 2016 Luke Zhao <me@lkzhao.com>
  6. //
  7. // Permission is hereby granted, free of charge, to any person obtaining a copy
  8. // of this software and associated documentation files (the "Software"), to deal
  9. // in the Software without restriction, including without limitation the rights
  10. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. // copies of the Software, and to permit persons to whom the Software is
  12. // furnished to do so, subject to the following conditions:
  13. //
  14. // The above copyright notice and this permission notice shall be included in
  15. // all copies or substantial portions of the Software.
  16. //
  17. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23. // THE SOFTWARE.
  24. import UIKit
  25. public enum CascadeDirection {
  26. case topToBottom
  27. case bottomToTop
  28. case leftToRight
  29. case rightToLeft
  30. case radial(center:CGPoint)
  31. case inverseRadial(center:CGPoint)
  32. var comparator: (UIView, UIView) -> Bool {
  33. switch self {
  34. case .topToBottom:
  35. return topToBottomComperator
  36. case .bottomToTop:
  37. return bottomToTopComperator
  38. case .leftToRight:
  39. return leftToRightComperator
  40. case .rightToLeft:
  41. return rightToLeftComperator
  42. case .radial(let center):
  43. return { (lhs: UIView, rhs: UIView) -> Bool in
  44. return lhs.center.distance(center) < rhs.center.distance(center)
  45. }
  46. case .inverseRadial(let center):
  47. return { (lhs: UIView, rhs: UIView) -> Bool in
  48. return lhs.center.distance(center) > rhs.center.distance(center)
  49. }
  50. }
  51. }
  52. init?(_ string: String) {
  53. switch string {
  54. case "bottomToTop":
  55. self = .bottomToTop
  56. case "leftToRight":
  57. self = .leftToRight
  58. case "rightToLeft":
  59. self = .rightToLeft
  60. case "topToBottom":
  61. self = .topToBottom
  62. default:
  63. return nil
  64. }
  65. }
  66. private func topToBottomComperator(lhs: UIView, rhs: UIView) -> Bool {
  67. return lhs.frame.minY < rhs.frame.minY
  68. }
  69. private func bottomToTopComperator(lhs: UIView, rhs: UIView) -> Bool {
  70. return lhs.frame.maxY == rhs.frame.maxY ? lhs.frame.maxX > rhs.frame.maxX : lhs.frame.maxY > rhs.frame.maxY
  71. }
  72. private func leftToRightComperator(lhs: UIView, rhs: UIView) -> Bool {
  73. return lhs.frame.minX < rhs.frame.minX
  74. }
  75. private func rightToLeftComperator(lhs: UIView, rhs: UIView) -> Bool {
  76. return lhs.frame.maxX > rhs.frame.maxX
  77. }
  78. }
  79. class CascadePreprocessor: BasePreprocessor {
  80. override func process(fromViews: [UIView], toViews: [UIView]) {
  81. process(views: fromViews)
  82. process(views: toViews)
  83. }
  84. func process(views: [UIView]) {
  85. for view in views {
  86. guard let (deltaTime, direction, delayMatchedViews) = context[view]?.cascade else { continue }
  87. var parentView = view
  88. if view is UITableView, let wrapperView = view.subviews.get(0) {
  89. parentView = wrapperView
  90. }
  91. let sortedSubviews = parentView.subviews.sorted(by: direction.comparator)
  92. let initialDelay = context[view]!.delay
  93. let finalDelay = TimeInterval(sortedSubviews.count) * deltaTime + initialDelay
  94. for (i, subview) in sortedSubviews.enumerated() {
  95. let delay = TimeInterval(i) * deltaTime + initialDelay
  96. func applyDelay(view: UIView) {
  97. if context.pairedView(for: view) == nil {
  98. context[view]?.delay = delay
  99. } else if delayMatchedViews, let paired = context.pairedView(for: view) {
  100. context[view]?.delay = finalDelay
  101. context[paired]?.delay = finalDelay
  102. }
  103. for subview in view.subviews {
  104. applyDelay(view: subview)
  105. }
  106. }
  107. applyDelay(view: subview)
  108. }
  109. }
  110. }
  111. }