Nenhuma Descrição

NavigationBar.swift 6.9KB

    // // NavigationBar.swift // PaiaiUIKit // // Created by ffib on 2019/4/23. // Copyright © 2019 FFIB. All rights reserved. // 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 } } /// NavigationBar transition 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 } } /// interactivePopGestureRecognizer 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 // if originShadowImage == nil { // shadowImageView.alpha = isInteractive ? (1 - percent) * 1 : 0 // } else { // shadowImageView.alpha = isInteractive ? percent * 1 : 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 } }