123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 |
- import UIKit
- class NavigationBar: UINavigationBar {
-
- private var currBgImage: UIImage?
- private var targetBgImage: UIImage?
- private var originShadowImage: UIImage?
-
- private var hasChangedBgImage: Bool = false
- private var hasChangedShadow: Bool = false
- private var titleView: UIView?
-
- var needsInteractive: Bool {
- return hasChangedBgImage || hasChangedShadow
- }
- var bounce: Bounce = .none
- var isPush: Bool = true
-
- override var shadowImage: UIImage? {
- didSet {
- originShadowImage = oldValue
- hasChangedShadow = true
- }
- }
-
- lazy var fakeView: UIVisualEffectView = {
- let v = UIVisualEffectView(effect: UIBlurEffect(style: .light))
- v.frame = getNavigationBarBounds()
- v.isUserInteractionEnabled = false
- v.autoresizingMask = [.flexibleWidth, .flexibleHeight]
-
- return v
- }()
-
- lazy var backgroundImageView: UIImageView = {
- let v = UIImageView()
- v.isUserInteractionEnabled = false
- v.frame = getNavigationBarBounds().offsetBy(dx: bounds.width, dy: 0)
-
- return v
- }()
-
- lazy var shadowImageView: UIImageView = {
- let v = UIImageView()
- v.frame = CGRect(x: 0, y: bounds.height, width: bounds.width, height: 0.5)
-
- return v
- }()
-
- override init(frame: CGRect) {
- super.init(frame: frame)
- }
-
- required init?(coder aDecoder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
- override func setBackgroundImage(_ backgroundImage: UIImage?, for barMetrics: UIBarMetrics) {
- currBgImage = self.backgroundImage(for: .default)
- targetBgImage = backgroundImage
- hasChangedBgImage = true
- }
-
- override func setBackgroundImage(_ backgroundImage: UIImage?, for barPosition: UIBarPosition, barMetrics: UIBarMetrics) {
- currBgImage = self.backgroundImage(for: .default)
- targetBgImage = backgroundImage
- hasChangedBgImage = true
- }
-
- func setBackgroundImage() {
- super.setBackgroundImage(targetBgImage, for: .any, barMetrics: .default)
- }
-
- func getBarBackground() -> UIView? {
- return value(forKeyPath: "_backgroundView") as? UIView
- }
-
- func getContentView() -> UIView? {
- for v in subviews {
- if let ContentClass = NSClassFromString("_UINavigationBarContentView"), v.isKind(of: ContentClass) {
- return v
- }
- }
- return nil
- }
-
- func getShadowView() -> UIView? {
- guard let barBackground = getBarBackground() else { return nil }
- for v in barBackground.subviews {
- if (v.bounds.height == 0.5) {
- return v
- }
- }
- return nil
- }
-
- func getNavigationBarBounds() -> CGRect {
- let statusHeight = UIApplication.shared.statusBarFrame.height
- return CGRect(x: 0, y: -statusHeight, width: bounds.width, height: bounds.height + statusHeight)
- }
-
- override func value(forUndefinedKey key: String) -> Any? {
- return nil
- }
- }
- extension NavigationBar {
- func constructViewHierarchy() {
- setupShadowView()
- setupBackgroundImageView()
-
- guard let barBackground = getBarBackground() else { return }
-
- insertSubview(shadowImageView, aboveSubview: barBackground)
- insertSubview(backgroundImageView, aboveSubview: barBackground)
- insertSubview(fakeView, belowSubview: backgroundImageView)
- layoutIfNeeded()
- }
-
- private func setupShadowView() {
- if let image = originShadowImage, image.size == CGSize.zero {
- shadowImageView.image = image
- } else {
- shadowImageView.backgroundColor = UIColor(red: 0, green: 0, blue: 0,
- alpha: 77.0 / 255)
- }
-
- shadowImageView.alpha = originShadowImage == nil ? 1 : 0
- getShadowView()?.isHidden = true
- }
-
- private func setupBackgroundImageView() {
- if isPush {
- fakeView.alpha = 0
- backgroundImageView.image = targetBgImage
- } else {
- setBackgroundImage()
- fakeView.alpha = 1
- backgroundImageView.image = currBgImage
- }
- }
-
-
- func transitionAnimationWithPercent(_ percent: CGFloat) {
- switch bounce {
- case .forward, .none:
- transitionShadowAnimationWithPercent(percent)
- transitionBackgroundAnimationWithPercent(percent)
- break
- case .backward:
- transitionShadowAnimationWithPercent(1 - percent)
- transitionBackgroundAnimationWithPercent(1 - percent)
- break
- }
- }
-
- func transitionAnimation() {
- transitionShadowAnimation()
- transitionBackgroundAnimation()
- }
-
- private func transitionShadowAnimation() {
- guard hasChangedShadow else { return }
- shadowImageView.alpha = originShadowImage == nil ? 0 : 1
- }
-
- private func transitionBackgroundAnimation() {
- guard hasChangedBgImage else { return }
- if isPush {
- fakeView.alpha = 1
- backgroundImageView.frame.origin.x = 0
- } else {
- fakeView.alpha = 0
- backgroundImageView.frame.origin.x = bounds.width
- }
- }
-
- private func transitionShadowAnimationWithPercent(_ percent: CGFloat) {
- guard hasChangedShadow else { return }
- shadowImageView.alpha = originShadowImage == nil ? (1 - percent) * 1 : percent * 1
- }
-
- private func transitionBackgroundAnimationWithPercent(_ percent: CGFloat) {
- guard hasChangedBgImage else { return }
- fakeView.alpha = (1 - percent) * 1
- backgroundImageView.frame.origin.x = percent * bounds.width
- }
-
- func clear() {
-
- if isPush && hasChangedBgImage { setBackgroundImage() }
-
- if !isPush && bounce == .backward && hasChangedBgImage {
- targetBgImage = currBgImage
- setBackgroundImage()
- }
-
- getShadowView()?.isHidden = false
-
- fakeView.removeFromSuperview()
- shadowImageView.removeFromSuperview()
- backgroundImageView.removeFromSuperview()
-
- currBgImage = nil
- targetBgImage = nil
-
- hasChangedBgImage = false
- hasChangedShadow = false
-
- bounce = .none
- }
- }
- extension NavigationBar {
- enum Bounce {
- case none
- case backward
- case forward
- }
- }
|