Swift 版本增加黑名单功能
shen0607 opened this issue · comments
`//
// WRNavigationBar.swift
// WRNavigationBar_swift
//
// Created by wangrui on 2017/4/19.
// Copyright © 2017年 wangrui. All rights reserved.
//
// Github地址:https://github.com/wangrui460/WRNavigationBar_swift
import UIKit
extension UINavigationBar:WRAwakeProtocol
{
fileprivate struct AssociatedKeys {
static var backgroundView: UIView = UIView()
static var backgroundImageView: UIImageView = UIImageView()
}
fileprivate var backgroundView:UIView? {
get {
guard let bgView = objc_getAssociatedObject(self, &AssociatedKeys.backgroundView) as? UIView else {
return nil
}
return bgView
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.backgroundView, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
fileprivate var backgroundImageView:UIImageView? {
get {
guard let bgImageView = objc_getAssociatedObject(self, &AssociatedKeys.backgroundImageView) as? UIImageView else {
return nil
}
return bgImageView
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.backgroundImageView, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
// set navigationBar backgroundImage
fileprivate func wr_setBackgroundImage(image:UIImage)
{
backgroundView?.removeFromSuperview()
backgroundView = nil
if (backgroundImageView == nil)
{
// add a image(nil color) to _UIBarBackground make it clear
setBackgroundImage(UIImage(), for: .default)
backgroundImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: Int(bounds.width), height: WRNavigationBar.navBarBottom()))
backgroundImageView?.autoresizingMask = .flexibleWidth
// _UIBarBackground is first subView for navigationBar
subviews.first?.insertSubview(backgroundImageView ?? UIImageView(), at: 0)
}
backgroundImageView?.image = image
}
// set navigationBar barTintColor
fileprivate func wr_setBackgroundColor(color:UIColor)
{
// 注释掉,不然导航栏会闪一下
backgroundImageView?.removeFromSuperview()
backgroundImageView = nil
if (backgroundView == nil)
{
// add a image(nil color) to _UIBarBackground make it clear
setBackgroundImage(UIImage(), for: .default)
backgroundView = UIView(frame: CGRect(x: 0, y: 0, width: Int(bounds.width), height: WRNavigationBar.navBarBottom()))
backgroundView?.autoresizingMask = .flexibleWidth
// _UIBarBackground is first subView for navigationBar
subviews.first?.insertSubview(backgroundView ?? UIView(), at: 0)
}
backgroundView?.backgroundColor = color
}
// set _UIBarBackground alpha (_UIBarBackground subviews alpha <= _UIBarBackground alpha)
fileprivate func wr_setBackgroundAlpha(alpha:CGFloat)
{
if let barBackgroundView = subviews.first
{
if #available(iOS 11.0, *)
{ // sometimes we can't change _UIBarBackground alpha
for view in barBackgroundView.subviews {
view.alpha = alpha
}
} else {
barBackgroundView.alpha = alpha
}
}
}
// 设置导航栏所有BarButtonItem的透明度
func wr_setBarButtonItemsAlpha(alpha:CGFloat, hasSystemBackIndicator:Bool)
{
for view in subviews
{
if (hasSystemBackIndicator == true)
{
// _UIBarBackground/_UINavigationBarBackground对应的view是系统导航栏,不需要改变其透明度
if let _UIBarBackgroundClass = NSClassFromString("_UIBarBackground")
{
if view.isKind(of: _UIBarBackgroundClass) == false {
view.alpha = alpha
}
}
if let _UINavigationBarBackground = NSClassFromString("_UINavigationBarBackground")
{
if view.isKind(of: _UINavigationBarBackground) == false {
view.alpha = alpha
}
}
}
else
{
// 这里如果不做判断的话,会显示 backIndicatorImage(系统返回按钮)
if let _UINavigationBarBackIndicatorViewClass = NSClassFromString("_UINavigationBarBackIndicatorView"),
view.isKind(of: _UINavigationBarBackIndicatorViewClass) == false
{
if let _UIBarBackgroundClass = NSClassFromString("_UIBarBackground")
{
if view.isKind(of: _UIBarBackgroundClass) == false {
view.alpha = alpha
}
}
if let _UINavigationBarBackground = NSClassFromString("_UINavigationBarBackground")
{
if view.isKind(of: _UINavigationBarBackground) == false {
view.alpha = alpha
}
}
}
}
}
}
/// 设置导航栏在垂直方向上平移多少距离
func wr_setTranslationY(translationY:CGFloat)
{
transform = CGAffineTransform.init(translationX: 0, y: translationY)
}
func wr_getTranslationY() -> CGFloat
{
return transform.ty
}
// call swizzling methods active 主动调用交换方法
private static let onceToken = UUID().uuidString
public static func wrAwake()
{
DispatchQueue.once(token: onceToken)
{
let needSwizzleSelectorArr = [
#selector(setter: titleTextAttributes)
]
for selector in needSwizzleSelectorArr {
let str = ("wr_" + selector.description)
if let originalMethod = class_getInstanceMethod(self, selector),
let swizzledMethod = class_getInstanceMethod(self, Selector(str)) {
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}
}
}
//==========================================================================
// MARK: swizzling pop
//==========================================================================
@objc func wr_setTitleTextAttributes(_ newTitleTextAttributes:[String : Any]?)
{
guard var attributes = newTitleTextAttributes else {
return
}
guard let originTitleTextAttributes = titleTextAttributes else {
wr_setTitleTextAttributes(attributes)
return
}
var titleColor:UIColor?
for attribute in originTitleTextAttributes {
if attribute.key == NSAttributedStringKey.foregroundColor {
titleColor = attribute.value as? UIColor
break
}
}
guard let originTitleColor = titleColor else {
wr_setTitleTextAttributes(attributes)
return
}
if attributes[NSAttributedStringKey.foregroundColor.rawValue] == nil {
attributes.updateValue(originTitleColor, forKey: NSAttributedStringKey.foregroundColor.rawValue)
}
wr_setTitleTextAttributes(attributes)
}
}
//==========================================================================
// MARK: - UINavigationController
//==========================================================================
extension UINavigationController: WRFatherAwakeProtocol
{
override open var preferredStatusBarStyle: UIStatusBarStyle {
return topViewController?.wrStatusBarStyle ?? WRNavigationBar.defaultStatusBarStyle
}
fileprivate func setNeedsNavigationBarUpdate(backgroundImage: UIImage)
{
navigationBar.wr_setBackgroundImage(image: backgroundImage)
}
fileprivate func setNeedsNavigationBarUpdate(barTintColor: UIColor)
{
navigationBar.wr_setBackgroundColor(color: barTintColor)
}
fileprivate func setNeedsNavigationBarUpdate(barBackgroundAlpha: CGFloat)
{
navigationBar.wr_setBackgroundAlpha(alpha: barBackgroundAlpha)
}
fileprivate func setNeedsNavigationBarUpdate(tintColor: UIColor) {
navigationBar.tintColor = tintColor
}
fileprivate func setNeedsNavigationBarUpdate(hideShadowImage: Bool)
{
navigationBar.shadowImage = (hideShadowImage == true) ? UIImage() : nil
}
fileprivate func setNeedsNavigationBarUpdate(titleColor: UIColor)
{
guard let titleTextAttributes = navigationBar.titleTextAttributes else {
navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor:titleColor]
return
}
var newTitleTextAttributes = titleTextAttributes
newTitleTextAttributes.updateValue(titleColor, forKey: NSAttributedStringKey.foregroundColor)
navigationBar.titleTextAttributes = newTitleTextAttributes
}
fileprivate func updateNavigationBar(fromVC: UIViewController?, toVC: UIViewController?, progress: CGFloat)
{
// change navBarBarTintColor
let fromBarTintColor = fromVC?.navBarBarTintColor ?? WRNavigationBar.defaultNavBarBarTintColor
let toBarTintColor = toVC?.navBarBarTintColor ?? WRNavigationBar.defaultNavBarBarTintColor
let newBarTintColor = WRNavigationBar.middleColor(fromColor: fromBarTintColor, toColor: toBarTintColor, percent: progress)
// setNeedsNavigationBarUpdate(barTintColor: newBarTintColor)
// modify by sxiangyu
if WRNavigationBar.needUpdateNavigationBar(vc: fromVC) || WRNavigationBar.needUpdateNavigationBar(vc: toVC) {
setNeedsNavigationBarUpdate(barTintColor: newBarTintColor)
}
// change navBarTintColor
let fromTintColor = fromVC?.navBarTintColor ?? WRNavigationBar.defaultNavBarTintColor
let toTintColor = toVC?.navBarTintColor ?? WRNavigationBar.defaultNavBarTintColor
let newTintColor = WRNavigationBar.middleColor(fromColor: fromTintColor, toColor: toTintColor, percent: progress)
// setNeedsNavigationBarUpdate(tintColor: newTintColor)
// modify by sxiangyu
if WRNavigationBar.needUpdateNavigationBar(vc: fromVC) {
setNeedsNavigationBarUpdate(tintColor: newTintColor)
}
// change navBarTitleColor
// let fromTitleColor = fromVC?.navBarTitleColor ?? WRNavigationBar.defaultNavBarTitleColor
// let toTitleColor = toVC?.navBarTitleColor ?? WRNavigationBar.defaultNavBarTitleColor
// let newTitleColor = WRNavigationBar.middleColor(fromColor: fromTitleColor, toColor: toTitleColor, percent: progress)
// setNeedsNavigationBarUpdate(titleColor: newTitleColor)
// change navBar _UIBarBackground alpha
let fromBarBackgroundAlpha = fromVC?.navBarBackgroundAlpha ?? WRNavigationBar.defaultBackgroundAlpha
let toBarBackgroundAlpha = toVC?.navBarBackgroundAlpha ?? WRNavigationBar.defaultBackgroundAlpha
let newBarBackgroundAlpha = WRNavigationBar.middleAlpha(fromAlpha: fromBarBackgroundAlpha, toAlpha: toBarBackgroundAlpha, percent: progress)
setNeedsNavigationBarUpdate(barBackgroundAlpha: newBarBackgroundAlpha)
}
// call swizzling methods active 主动调用交换方法
private static let onceToken = UUID().uuidString
public static func fatherAwake()
{
DispatchQueue.once(token: onceToken)
{
let needSwizzleSelectorArr = [
NSSelectorFromString("_updateInteractiveTransition:"),
#selector(popToViewController),
#selector(popToRootViewController),
#selector(pushViewController)
]
for selector in needSwizzleSelectorArr {
// _updateInteractiveTransition: => wr_updateInteractiveTransition:
let str = ("wr_" + selector.description).replacingOccurrences(of: "__", with: "_")
if let originalMethod = class_getInstanceMethod(self, selector),
let swizzledMethod = class_getInstanceMethod(self, Selector(str)) {
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}
}
}
//==========================================================================
// MARK: swizzling pop
//==========================================================================
struct popProperties {
fileprivate static let popDuration = 0.13
fileprivate static var displayCount = 0
fileprivate static var popProgress:CGFloat {
let all:CGFloat = CGFloat(60.0 * popDuration)
let current = min(all, CGFloat(displayCount))
return current / all
}
}
// swizzling system method: popToViewController
@objc func wr_popToViewController(_ viewController: UIViewController, animated: Bool) -> [UIViewController]?
{
setNeedsNavigationBarUpdate(titleColor: viewController.navBarTitleColor)
var displayLink:CADisplayLink? = CADisplayLink(target: self, selector: #selector(popNeedDisplay))
// UITrackingRunLoopMode: 界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
// NSRunLoopCommonModes contains kCFRunLoopDefaultMode and UITrackingRunLoopMode
displayLink?.add(to: RunLoop.main, forMode: .commonModes)
CATransaction.setCompletionBlock {
displayLink?.invalidate()
displayLink = nil
popProperties.displayCount = 0
}
CATransaction.setAnimationDuration(popProperties.popDuration)
CATransaction.begin()
let vcs = wr_popToViewController(viewController, animated: animated)
CATransaction.commit()
return vcs
}
// swizzling system method: popToRootViewControllerAnimated
@objc func wr_popToRootViewControllerAnimated(_ animated: Bool) -> [UIViewController]?
{
var displayLink:CADisplayLink? = CADisplayLink(target: self, selector: #selector(popNeedDisplay))
displayLink?.add(to: RunLoop.main, forMode: .commonModes)
CATransaction.setCompletionBlock {
displayLink?.invalidate()
displayLink = nil
popProperties.displayCount = 0
}
CATransaction.setAnimationDuration(popProperties.popDuration)
CATransaction.begin()
let vcs = wr_popToRootViewControllerAnimated(animated)
CATransaction.commit()
return vcs;
}
// change navigationBar barTintColor smooth before pop to current VC finished
@objc fileprivate func popNeedDisplay()
{
guard let topViewController = topViewController,
let coordinator = topViewController.transitionCoordinator else {
return
}
popProperties.displayCount += 1
let popProgress = popProperties.popProgress
// print("第\(popProperties.displayCount)次pop的进度:\(popProgress)")
let fromVC = coordinator.viewController(forKey: .from)
let toVC = coordinator.viewController(forKey: .to)
updateNavigationBar(fromVC: fromVC, toVC: toVC, progress: popProgress)
}
//==========================================================================
// MARK: swizzling push
//==========================================================================
struct pushProperties {
fileprivate static let pushDuration = 0.13
fileprivate static var displayCount = 0
fileprivate static var pushProgress:CGFloat {
let all:CGFloat = CGFloat(60.0 * pushDuration)
let current = min(all, CGFloat(displayCount))
return current / all
}
}
// swizzling system method: pushViewController
@objc func wr_pushViewController(_ viewController: UIViewController, animated: Bool)
{
var displayLink:CADisplayLink? = CADisplayLink(target: self, selector: #selector(pushNeedDisplay))
displayLink?.add(to: RunLoop.main, forMode: .commonModes)
CATransaction.setCompletionBlock {
displayLink?.invalidate()
displayLink = nil
pushProperties.displayCount = 0
viewController.pushToCurrentVCFinished = true
};
CATransaction.setAnimationDuration(pushProperties.pushDuration)
CATransaction.begin()
wr_pushViewController(viewController, animated: animated)
CATransaction.commit()
}
// change navigationBar barTintColor smooth before push to current VC finished or before pop to current VC finished
@objc fileprivate func pushNeedDisplay()
{
guard let topViewController = topViewController,
let coordinator = topViewController.transitionCoordinator else {
return
}
pushProperties.displayCount += 1
let pushProgress = pushProperties.pushProgress
// print("第\(pushProperties.displayCount)次push的进度:\(pushProgress)")
let fromVC = coordinator.viewController(forKey: .from)
let toVC = coordinator.viewController(forKey: .to)
updateNavigationBar(fromVC: fromVC, toVC: toVC, progress: pushProgress)
}
}
//==========================================================================
// MARK: - deal the gesture of return
//==========================================================================
extension UINavigationController: UINavigationBarDelegate
{
public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool
{
if let topVC = topViewController,
let coor = topVC.transitionCoordinator, coor.initiallyInteractive {
if #available(iOS 10.0, *) {
coor.notifyWhenInteractionChanges({ (context) in
self.dealInteractionChanges(context)
})
} else {
coor.notifyWhenInteractionEnds({ (context) in
self.dealInteractionChanges(context)
})
}
return true
}
let itemCount = navigationBar.items?.count ?? 0
let n = viewControllers.count >= itemCount ? 2 : 1
let popToVC = viewControllers[viewControllers.count - n]
popToViewController(popToVC, animated: true)
return true
}
// deal the gesture of return break off
private func dealInteractionChanges(_ context: UIViewControllerTransitionCoordinatorContext)
{
let animations: (UITransitionContextViewControllerKey) -> () = {
let curColor = context.viewController(forKey: $0)?.navBarBarTintColor ?? WRNavigationBar.defaultNavBarBarTintColor
let curAlpha = context.viewController(forKey: $0)?.navBarBackgroundAlpha ?? WRNavigationBar.defaultBackgroundAlpha
self.setNeedsNavigationBarUpdate(barTintColor: curColor)
self.setNeedsNavigationBarUpdate(barBackgroundAlpha: curAlpha)
}
// after that, cancel the gesture of return
if context.isCancelled
{
let cancelDuration: TimeInterval = context.transitionDuration * Double(context.percentComplete)
UIView.animate(withDuration: cancelDuration) {
animations(.from)
}
}
else
{
// after that, finish the gesture of return
let finishDuration: TimeInterval = context.transitionDuration * Double(1 - context.percentComplete)
UIView.animate(withDuration: finishDuration) {
animations(.to)
}
}
}
// swizzling system method: _updateInteractiveTransition
@objc func wr_updateInteractiveTransition(_ percentComplete: CGFloat)
{
guard let topViewController = topViewController,
let coordinator = topViewController.transitionCoordinator else {
wr_updateInteractiveTransition(percentComplete)
return
}
let fromVC = coordinator.viewController(forKey: .from)
let toVC = coordinator.viewController(forKey: .to)
updateNavigationBar(fromVC: fromVC, toVC: toVC, progress: percentComplete)
wr_updateInteractiveTransition(percentComplete)
}
}
//=============================================================================
// MARK: - store navigationBar barTintColor and tintColor every viewController
//=============================================================================
extension UIViewController: WRAwakeProtocol
{
fileprivate struct AssociatedKeys
{
static var pushToCurrentVCFinished: Bool = false
static var pushToNextVCFinished:Bool = false
static var navBarBackgroundImage: UIImage = UIImage()
static var navBarBarTintColor: UIColor = WRNavigationBar.defaultNavBarBarTintColor
static var navBarBackgroundAlpha:CGFloat = 1.0
static var navBarTintColor: UIColor = WRNavigationBar.defaultNavBarTintColor
static var navBarTitleColor: UIColor = WRNavigationBar.defaultNavBarTitleColor
static var wrStatusBarStyle: UIStatusBarStyle = UIStatusBarStyle.default
static var navBarShadowImageHidden: Bool = false
static var customNavBar: UINavigationBar = UINavigationBar()
}
// navigationBar barTintColor can not change by currentVC before fromVC push to currentVC finished
fileprivate var pushToCurrentVCFinished:Bool {
get {
guard let isFinished = objc_getAssociatedObject(self, &AssociatedKeys.pushToCurrentVCFinished) as? Bool else {
return false
}
return isFinished
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.pushToCurrentVCFinished, newValue, .OBJC_ASSOCIATION_ASSIGN)
}
}
// navigationBar barTintColor can not change by currentVC when currentVC push to nextVC finished
fileprivate var pushToNextVCFinished:Bool {
get {
guard let isFinished = objc_getAssociatedObject(self, &AssociatedKeys.pushToNextVCFinished) as? Bool else {
return false
}
return isFinished
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.pushToNextVCFinished, newValue, .OBJC_ASSOCIATION_ASSIGN)
}
}
// you can set navigationBar backgroundImage
var navBarBackgroundImage: UIImage?
{
get {
guard let bgImage = objc_getAssociatedObject(self, &AssociatedKeys.navBarBackgroundImage) as? UIImage else {
return WRNavigationBar.defaultNavBarBackgroundImage
}
return bgImage
}
// set {
// if customNavBar.isKind(of: UINavigationBar.self) {
// let navBar = customNavBar as! UINavigationBar
// navBar.wr_setBackgroundImage(image: newValue!)
// }
// else {
// objc_setAssociatedObject(self, &AssociatedKeys.navBarBackgroundImage, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
// }
// }
}
// navigationBar barTintColor
var navBarBarTintColor: UIColor {
get {
/// add by sxiangyu
guard WRNavigationBar.needUpdateNavigationBar(vc: self) else {
return self.navigationController?.navigationBar.barTintColor ?? WRNavigationBar.defaultNavBarBarTintColor
}
guard let barTintColor = objc_getAssociatedObject(self, &AssociatedKeys.navBarBarTintColor) as? UIColor else {
return WRNavigationBar.defaultNavBarBarTintColor
}
return barTintColor
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.navBarBarTintColor, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
if customNavBar.isKind(of: UINavigationBar.self) {
// let navBar = customNavBar as! UINavigationBar
// navBar.wr_setBackgroundColor(color: newValue)
}
else {
if canUpdateNavBarBarTintColorOrBackgroundAlpha == true {
navigationController?.setNeedsNavigationBarUpdate(barTintColor: newValue)
}
}
}
}
// navigationBar _UIBarBackground alpha
var navBarBackgroundAlpha:CGFloat {
get {
guard let barBackgroundAlpha = objc_getAssociatedObject(self, &AssociatedKeys.navBarBackgroundAlpha) as? CGFloat else {
return 1.0
}
return barBackgroundAlpha
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.navBarBackgroundAlpha, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
if customNavBar.isKind(of: UINavigationBar.self) {
// let navBar = customNavBar as! UINavigationBar
// navBar.wr_setBackgroundAlpha(alpha: newValue)
}
else {
if canUpdateNavBarBarTintColorOrBackgroundAlpha == true {
navigationController?.setNeedsNavigationBarUpdate(barBackgroundAlpha: newValue)
}
}
}
}
private var canUpdateNavBarBarTintColorOrBackgroundAlpha:Bool {
get {
let isRootViewController = self.navigationController?.viewControllers.first == self
if (pushToCurrentVCFinished == true || isRootViewController == true) && pushToNextVCFinished == false {
return true
} else {
return false
}
}
}
// navigationBar tintColor
var navBarTintColor: UIColor {
get {
/// add by sxiangyu
guard WRNavigationBar.needUpdateNavigationBar(vc: self) else {
return self.navigationController?.navigationBar.tintColor ?? WRNavigationBar.defaultNavBarTintColor
}
guard let tintColor = objc_getAssociatedObject(self, &AssociatedKeys.navBarTintColor) as? UIColor else {
return WRNavigationBar.defaultNavBarTintColor
}
return tintColor
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.navBarTintColor, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
if customNavBar.isKind(of: UINavigationBar.self) {
// let navBar = customNavBar as! UINavigationBar
// navBar.tintColor = newValue
}
else
{
if pushToNextVCFinished == false {
navigationController?.setNeedsNavigationBarUpdate(tintColor: newValue)
}
}
}
}
// navigationBar titleColor
var navBarTitleColor: UIColor {
get {
/// add by sxiangyu
guard WRNavigationBar.needUpdateNavigationBar(vc: self) else {
return (self.navigationController?.navigationBar.titleTextAttributes?[NSAttributedStringKey.foregroundColor] as? UIColor) ?? WRNavigationBar.defaultNavBarTitleColor
}
guard let titleColor = objc_getAssociatedObject(self, &AssociatedKeys.navBarTitleColor) as? UIColor else {
return WRNavigationBar.defaultNavBarTitleColor
}
return titleColor
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.navBarTitleColor, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
if customNavBar.isKind(of: UINavigationBar.self) {
// let navBar = customNavBar as! UINavigationBar
// navBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor:newValue]
}
else
{
if pushToNextVCFinished == false {
navigationController?.setNeedsNavigationBarUpdate(titleColor: newValue)
}
}
}
}
// statusBarStyle
var wrStatusBarStyle: UIStatusBarStyle {
get {
guard let style = objc_getAssociatedObject(self, &AssociatedKeys.wrStatusBarStyle) as? UIStatusBarStyle else {
return WRNavigationBar.defaultStatusBarStyle
}
return style
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.wrStatusBarStyle, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
setNeedsStatusBarAppearanceUpdate()
}
}
// if you want shadowImage hidden,you can via hideShadowImage = true
var navBarShadowImageHidden:Bool {
get {
guard let isHidden = objc_getAssociatedObject(self, &AssociatedKeys.navBarShadowImageHidden) as? Bool else {
return WRNavigationBar.defaultShadowImageHidden
}
return isHidden
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.navBarShadowImageHidden, newValue, .OBJC_ASSOCIATION_ASSIGN)
navigationController?.setNeedsNavigationBarUpdate(hideShadowImage: newValue)
}
}
// custom navigationBar
var customNavBar: UIView {
get {
guard let navBar = objc_getAssociatedObject(self, &AssociatedKeys.customNavBar) as? UINavigationBar else {
return UIView()
}
return navBar
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.customNavBar, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
// swizzling two system methods: viewWillAppear(_:) and viewWillDisappear(_:)
private static let onceToken = UUID().uuidString
@objc public static func wrAwake()
{
DispatchQueue.once(token: onceToken)
{
let needSwizzleSelectors = [
#selector(viewWillAppear(_:)),
#selector(viewWillDisappear(_:)),
#selector(viewDidAppear(_:))
]
for selector in needSwizzleSelectors
{
let newSelectorStr = "wr_" + selector.description
if let originalMethod = class_getInstanceMethod(self, selector),
let swizzledMethod = class_getInstanceMethod(self, Selector(newSelectorStr)) {
method_exchangeImplementations(originalMethod, swizzledMethod)
}
}
}
}
@objc func wr_viewWillAppear(_ animated: Bool)
{
if canUpdateNavigationBar() == true {
pushToNextVCFinished = false
navigationController?.setNeedsNavigationBarUpdate(tintColor: navBarTintColor)
navigationController?.setNeedsNavigationBarUpdate(titleColor: navBarTitleColor)
}
wr_viewWillAppear(animated)
}
@objc func wr_viewWillDisappear(_ animated: Bool)
{
if canUpdateNavigationBar() == true {
pushToNextVCFinished = true
}
wr_viewWillDisappear(animated)
}
@objc func wr_viewDidAppear(_ animated: Bool)
{
if self.navigationController?.viewControllers.first != self {
self.pushToCurrentVCFinished = true
}
if canUpdateNavigationBar() == true
{
if let navBarBgImage = navBarBackgroundImage {
navigationController?.setNeedsNavigationBarUpdate(backgroundImage: navBarBgImage)
} else {
navigationController?.setNeedsNavigationBarUpdate(barTintColor: navBarBarTintColor)
}
navigationController?.setNeedsNavigationBarUpdate(barBackgroundAlpha: navBarBackgroundAlpha)
navigationController?.setNeedsNavigationBarUpdate(tintColor: navBarTintColor)
navigationController?.setNeedsNavigationBarUpdate(titleColor: navBarTitleColor)
navigationController?.setNeedsNavigationBarUpdate(hideShadowImage: navBarShadowImageHidden)
}
wr_viewDidAppear(animated)
}
func canUpdateNavigationBar() -> Bool
{
/// add by sxiangyu
guard WRNavigationBar.needUpdateNavigationBar(vc: self) else {
return false
}
let viewFrame = view.frame
let maxFrame = UIScreen.main.bounds
let middleFrame = CGRect(x: 0, y: WRNavigationBar.navBarBottom(), width: WRNavigationBar.screenWidth(), height: WRNavigationBar.screenHeight()-WRNavigationBar.navBarBottom())
let minFrame = CGRect(x: 0, y: WRNavigationBar.navBarBottom(), width: WRNavigationBar.screenWidth(), height: WRNavigationBar.screenHeight()-WRNavigationBar.navBarBottom()-WRNavigationBar.tabBarHeight())
// 蝙蝠🦇
let isBat = viewFrame.equalTo(maxFrame) || viewFrame.equalTo(middleFrame) || viewFrame.equalTo(minFrame)
if self.navigationController != nil && isBat == true {
return true
} else {
return false
}
}
}
//====================================================================================
// MARK: - Swizzling会改变全局状态,所以用 DispatchQueue.once 来确保无论多少线程都只会被执行一次
//====================================================================================
extension DispatchQueue {
private static var onceTracker = [String]()
//Executes a block of code, associated with a unique token, only once. The code is thread safe and will only execute the code once even in the presence of multithreaded calls.
public class func once(token: String, block: () -> Void)
{ // 保证被 objc_sync_enter 和 objc_sync_exit 包裹的代码可以有序同步地执行
objc_sync_enter(self)
defer { // 作用域结束后执行defer中的代码
objc_sync_exit(self)
}
if onceTracker.contains(token) {
return
}
onceTracker.append(token)
block()
}
}
//===========================================================================================
// MARK: - default navigationBar barTintColor、tintColor and statusBarStyle YOU CAN CHANGE!!!
//===========================================================================================
class WRNavigationBar
{
fileprivate struct AssociatedKeys
{ // default is system attributes
static var defNavBarBarTintColor: UIColor = UIColor.white
static var defNavBarBackgroundImage: UIImage = UIImage()
static var defNavBarTintColor: UIColor = UIColor(red: 0, green: 0.478431, blue: 1, alpha: 1.0)
static var defNavBarTitleColor: UIColor = UIColor.black
static var defStatusBarStyle: UIStatusBarStyle = UIStatusBarStyle.default
static var defShadowImageHidden: Bool = false
}
class var defaultNavBarBarTintColor: UIColor {
get {
guard let def = objc_getAssociatedObject(self, &AssociatedKeys.defNavBarBarTintColor) as? UIColor else {
return AssociatedKeys.defNavBarBarTintColor
}
return def
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.defNavBarBarTintColor, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
class var defaultNavBarBackgroundImage: UIImage? {
get {
guard let def = objc_getAssociatedObject(self, &AssociatedKeys.defNavBarBackgroundImage) as? UIImage else {
return nil
}
return def
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.defNavBarBackgroundImage, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
class var defaultNavBarTintColor: UIColor {
get {
guard let def = objc_getAssociatedObject(self, &AssociatedKeys.defNavBarTintColor) as? UIColor else {
return AssociatedKeys.defNavBarTintColor
}
return def
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.defNavBarTintColor, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
class var defaultNavBarTitleColor: UIColor {
get {
guard let def = objc_getAssociatedObject(self, &AssociatedKeys.defNavBarTitleColor) as? UIColor else {
return AssociatedKeys.defNavBarTitleColor
}
return def
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.defNavBarTitleColor, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
class var defaultStatusBarStyle: UIStatusBarStyle {
get {
guard let def = objc_getAssociatedObject(self, &AssociatedKeys.defStatusBarStyle) as? UIStatusBarStyle else {
return .default
}
return def
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.defStatusBarStyle, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
class var defaultShadowImageHidden: Bool {
get {
guard let def = objc_getAssociatedObject(self, &AssociatedKeys.defShadowImageHidden) as? Bool else {
return false
}
return def
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.defShadowImageHidden, newValue, .OBJC_ASSOCIATION_ASSIGN)
}
}
class var defaultBackgroundAlpha: CGFloat {
get {
return 1.0
}
}
// Calculate the middle Color with translation percent
class fileprivate func middleColor(fromColor: UIColor, toColor: UIColor, percent: CGFloat) -> UIColor
{
// get current color RGBA
var fromRed: CGFloat = 0
var fromGreen: CGFloat = 0
var fromBlue: CGFloat = 0
var fromAlpha: CGFloat = 0
fromColor.getRed(&fromRed, green: &fromGreen, blue: &fromBlue, alpha: &fromAlpha)
// get to color RGBA
var toRed: CGFloat = 0
var toGreen: CGFloat = 0
var toBlue: CGFloat = 0
var toAlpha: CGFloat = 0
toColor.getRed(&toRed, green: &toGreen, blue: &toBlue, alpha: &toAlpha)
// calculate middle color RGBA
let newRed = fromRed + (toRed - fromRed) * percent
let newGreen = fromGreen + (toGreen - fromGreen) * percent
let newBlue = fromBlue + (toBlue - fromBlue) * percent
let newAlpha = fromAlpha + (toAlpha - fromAlpha) * percent
return UIColor(red: newRed, green: newGreen, blue: newBlue, alpha: newAlpha)
}
// Calculate the middle alpha
class fileprivate func middleAlpha(fromAlpha: CGFloat, toAlpha: CGFloat, percent: CGFloat) -> CGFloat
{
let newAlpha = fromAlpha + (toAlpha - fromAlpha) * percent
return newAlpha
}
/// add by sxiangyu 是否需要更新
class fileprivate func needUpdateNavigationBar(vc: UIViewController?) -> Bool {
if let _ = vc as? INNOBaseViewController {
return true
} else {
return false
}
}
}
extension WRNavigationBar
{
class func isIphoneX() -> Bool {
return UIScreen.main.bounds.equalTo(CGRect(x: 0, y: 0, width: 375, height: 812)) ||
UIScreen.main.bounds.equalTo(CGRect(x: 0, y: 0, width: 812, height: 375)) ||
UIScreen.main.bounds.equalTo(CGRect(x: 0, y: 0, width: 414, height: 896)) ||
UIScreen.main.bounds.equalTo(CGRect(x: 0, y: 0, width: 896, height: 414))
}
class func navBarBottom() -> Int {
return self.isIphoneX() ? 88 : 64;
}
class func tabBarHeight() -> Int {
return self.isIphoneX() ? 83 : 49;
}
class func screenWidth() -> Int {
return Int(UIScreen.main.bounds.size.width)
}
class func screenHeight() -> Int {
return Int(UIScreen.main.bounds.size.height)
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 1. 定义 WRAwakeProtocol 协议
public protocol WRAwakeProtocol: class {
static func wrAwake()
}
public protocol WRFatherAwakeProtocol: class
{ // 1.1 定义 WRFatherAwakeProtocol ()
static func fatherAwake()
}
class NothingToSeeHere
{
static func harmlessFunction(){
// let typeCount = Int(objc_getClassList(nil, 0))
// let types = UnsafeMutablePointer<AnyClass?>.allocate(capacity: typeCount)
// let autoreleaseintTypes = AutoreleasingUnsafeMutablePointer(types)
// objc_getClassList(autoreleaseintTypes, Int32(typeCount)) //获取所有的类
// for index in 0 ..< typeCount {
// (types[index] as? WRAwakeProtocol.Type)?.wrAwake() //如果该类实现了SelfAware协议,那么调用 awake 方法
// (types[index] as? WRFatherAwakeProtocol.Type)?.fatherAwake()
// }
// types.deallocate(capacity: typeCount)
// !!!这边不能按照注释的写循环,否则启动会卡住线程(15000多个循环)
// 实现了SelfAware协议,那么调用 awake 方法
UINavigationBar.wrAwake()
UIViewController.wrAwake()
UINavigationController.fatherAwake()
}
}
// 2. 让APP启动时只执行一次 harmlessFunction 方法
extension UIApplication
{
private static let runOnce:Void = { //使用静态属性以保证只调用一次(该属性是个方法)
NothingToSeeHere.harmlessFunction()
}()
open override var next: UIResponder?{ //重写next属性
UIApplication.runOnce
return super.next
}
}
// 3. 自定义类实现 WRAwakeProtocol 协议,重写 wrAwake 方法
// 自定义类实现 WRFatherAwakeProtocol 协议,重写 fatherAwake 方法
`
主要修改
`class fileprivate func needUpdateNavigationBar(vc: UIViewController?) -> Bool {
if let _ = vc as? INNOBaseViewController {
return true
} else {
return false
}
}`
这里面的方法,我这边是有个公共的baseVC ,所以这样判断,大家可以可以设置黑名单列表之类的,在这里面判断。主要模仿OC的方式。