Sin Descripción

NavigationBar.swift 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. //
  2. // NavigationBar.swift
  3. // PaiaiUIKit
  4. //
  5. // Created by FFIB on 2019/4/23.
  6. // Copyright © 2019 FFIB. All rights reserved.
  7. //
  8. import UIKit
  9. class NavigationBar: UINavigationBar {
  10. private var currBgImage: UIImage?
  11. private var targetBgImage: UIImage?
  12. private var originShadowImage: UIImage?
  13. private var hasChangedBgImage: Bool = false
  14. private var hasChangedShadow: Bool = false
  15. private var titleView: UIView?
  16. var needsInteractive: Bool {
  17. return hasChangedBgImage || hasChangedShadow
  18. }
  19. var bounce: Bounce = .none
  20. var isPush: Bool = true
  21. override var shadowImage: UIImage? {
  22. didSet {
  23. originShadowImage = oldValue
  24. hasChangedShadow = true
  25. }
  26. }
  27. lazy var fakeView: UIVisualEffectView = {
  28. let val = UIVisualEffectView(effect: UIBlurEffect(style: .light))
  29. val.frame = getNavigationBarBounds()
  30. val.isUserInteractionEnabled = false
  31. val.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  32. return val
  33. }()
  34. lazy var backgroundImageView: UIImageView = {
  35. let view = UIImageView()
  36. view.isUserInteractionEnabled = false
  37. view.frame = getNavigationBarBounds().offsetBy(dx: bounds.width, dy: 0)
  38. return view
  39. }()
  40. lazy var shadowImageView: UIImageView = {
  41. let view = UIImageView()
  42. view.frame = CGRect(x: 0, y: bounds.height, width: bounds.width, height: 0.5)
  43. return view
  44. }()
  45. override init(frame: CGRect) {
  46. super.init(frame: frame)
  47. }
  48. required init?(coder aDecoder: NSCoder) {
  49. fatalError("init(coder:) has not been implemented")
  50. }
  51. override func setBackgroundImage(_ backgroundImage: UIImage?, for barMetrics: UIBarMetrics) {
  52. currBgImage = self.backgroundImage(for: .default)
  53. targetBgImage = backgroundImage
  54. hasChangedBgImage = true
  55. }
  56. override func setBackgroundImage(_ backgroundImage: UIImage?,
  57. for barPosition: UIBarPosition,
  58. barMetrics: UIBarMetrics) {
  59. currBgImage = self.backgroundImage(for: .default)
  60. targetBgImage = backgroundImage
  61. hasChangedBgImage = true
  62. }
  63. func setBackgroundImage() {
  64. super.setBackgroundImage(targetBgImage, for: .any, barMetrics: .default)
  65. }
  66. func getBarBackground() -> UIView? {
  67. return value(forKeyPath: "_backgroundView") as? UIView
  68. }
  69. func getContentView() -> UIView? {
  70. for val in subviews {
  71. if let ContentClass = NSClassFromString("_UINavigationBarContentView"), val.isKind(of: ContentClass) {
  72. return val
  73. }
  74. }
  75. return nil
  76. }
  77. func getShadowView() -> UIView? {
  78. guard let barBackground = getBarBackground() else { return nil }
  79. for val in barBackground.subviews where val.bounds.height == 0.5 {
  80. return val
  81. }
  82. return nil
  83. }
  84. func getNavigationBarBounds() -> CGRect {
  85. let statusHeight = UIApplication.shared.statusBarFrame.height
  86. return CGRect(x: 0, y: -statusHeight, width: bounds.width, height: bounds.height + statusHeight)
  87. }
  88. override func value(forUndefinedKey key: String) -> Any? {
  89. return nil
  90. }
  91. }
  92. /// NavigationBar transition
  93. extension NavigationBar {
  94. func constructViewHierarchy() {
  95. setupShadowView()
  96. setupBackgroundImageView()
  97. guard let barBackground = getBarBackground() else { return }
  98. insertSubview(shadowImageView, aboveSubview: barBackground)
  99. insertSubview(backgroundImageView, aboveSubview: barBackground)
  100. insertSubview(fakeView, belowSubview: backgroundImageView)
  101. layoutIfNeeded()
  102. }
  103. private func setupShadowView() {
  104. if let image = originShadowImage, image.size == CGSize.zero {
  105. shadowImageView.image = image
  106. } else {
  107. shadowImageView.backgroundColor = UIColor(red: 0, green: 0, blue: 0,
  108. alpha: 77.0 / 255)
  109. }
  110. shadowImageView.alpha = originShadowImage == nil ? 1 : 0
  111. getShadowView()?.isHidden = true
  112. }
  113. private func setupBackgroundImageView() {
  114. if isPush {
  115. fakeView.alpha = 0
  116. backgroundImageView.image = targetBgImage
  117. } else {
  118. setBackgroundImage()
  119. fakeView.alpha = 1
  120. backgroundImageView.image = currBgImage
  121. }
  122. }
  123. /// interactivePopGestureRecognizer
  124. func transitionAnimationWithPercent(_ percent: CGFloat) {
  125. switch bounce {
  126. case .forward, .none:
  127. transitionShadowAnimationWithPercent(percent)
  128. transitionBackgroundAnimationWithPercent(percent)
  129. case .backward:
  130. transitionShadowAnimationWithPercent(1 - percent)
  131. transitionBackgroundAnimationWithPercent(1 - percent)
  132. }
  133. }
  134. func transitionAnimation() {
  135. transitionShadowAnimation()
  136. transitionBackgroundAnimation()
  137. }
  138. private func transitionShadowAnimation() {
  139. guard hasChangedShadow else { return }
  140. shadowImageView.alpha = originShadowImage == nil ? 0 : 1
  141. // if originShadowImage == nil {
  142. // shadowImageView.alpha = isInteractive ? (1 - percent) * 1 : 0
  143. // } else {
  144. // shadowImageView.alpha = isInteractive ? percent * 1 : 1
  145. // }
  146. }
  147. private func transitionBackgroundAnimation() {
  148. guard hasChangedBgImage else { return }
  149. if isPush {
  150. fakeView.alpha = 1
  151. backgroundImageView.frame.origin.x = 0
  152. } else {
  153. fakeView.alpha = 0
  154. backgroundImageView.frame.origin.x = bounds.width
  155. }
  156. }
  157. private func transitionShadowAnimationWithPercent(_ percent: CGFloat) {
  158. guard hasChangedShadow else { return }
  159. shadowImageView.alpha = originShadowImage == nil ? (1 - percent) * 1 : percent * 1
  160. }
  161. private func transitionBackgroundAnimationWithPercent(_ percent: CGFloat) {
  162. guard hasChangedBgImage else { return }
  163. fakeView.alpha = (1 - percent) * 1
  164. backgroundImageView.frame.origin.x = percent * bounds.width
  165. }
  166. func clear() {
  167. if isPush && hasChangedBgImage { setBackgroundImage() }
  168. if !isPush && bounce == .backward && hasChangedBgImage {
  169. targetBgImage = currBgImage
  170. setBackgroundImage()
  171. }
  172. getShadowView()?.isHidden = false
  173. fakeView.removeFromSuperview()
  174. shadowImageView.removeFromSuperview()
  175. backgroundImageView.removeFromSuperview()
  176. currBgImage = nil
  177. targetBgImage = nil
  178. hasChangedBgImage = false
  179. hasChangedShadow = false
  180. bounce = .none
  181. }
  182. }
  183. extension NavigationBar {
  184. enum Bounce {
  185. case none
  186. case backward
  187. case forward
  188. }
  189. }