WRNavigationBar.swift 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002
  1. //
  2. // WRNavigationBar.swift
  3. // WRNavigationBar_swift
  4. //
  5. // Created by wangrui on 2017/4/19.
  6. // Copyright © 2017年 wangrui. All rights reserved.
  7. //
  8. // Github地址:https://github.com/wangrui460/WRNavigationBar_swift
  9. import UIKit
  10. extension UINavigationBar:WRAwakeProtocol
  11. {
  12. fileprivate struct AssociatedKeys {
  13. static var backgroundView: UIView = UIView()
  14. static var backgroundImageView: UIImageView = UIImageView()
  15. }
  16. fileprivate var backgroundView:UIView? {
  17. get {
  18. guard let bgView = objc_getAssociatedObject(self, &AssociatedKeys.backgroundView) as? UIView else {
  19. return nil
  20. }
  21. return bgView
  22. }
  23. set {
  24. objc_setAssociatedObject(self, &AssociatedKeys.backgroundView, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  25. }
  26. }
  27. fileprivate var backgroundImageView:UIImageView? {
  28. get {
  29. guard let bgImageView = objc_getAssociatedObject(self, &AssociatedKeys.backgroundImageView) as? UIImageView else {
  30. return nil
  31. }
  32. return bgImageView
  33. }
  34. set {
  35. objc_setAssociatedObject(self, &AssociatedKeys.backgroundImageView, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  36. }
  37. }
  38. // set navigationBar backgroundImage
  39. fileprivate func wr_setBackgroundImage(image:UIImage)
  40. {
  41. backgroundView?.removeFromSuperview()
  42. backgroundView = nil
  43. if (backgroundImageView == nil)
  44. {
  45. // add a image(nil color) to _UIBarBackground make it clear
  46. setBackgroundImage(UIImage(), for: .default)
  47. backgroundImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: Int(bounds.width), height: WRNavigationBar.navBarBottom()))
  48. backgroundImageView?.autoresizingMask = .flexibleWidth
  49. // _UIBarBackground is first subView for navigationBar
  50. subviews.first?.insertSubview(backgroundImageView ?? UIImageView(), at: 0)
  51. }
  52. backgroundImageView?.image = image
  53. }
  54. // set navigationBar barTintColor
  55. fileprivate func wr_setBackgroundColor(color:UIColor)
  56. {
  57. backgroundImageView?.removeFromSuperview()
  58. backgroundImageView = nil
  59. if (backgroundView == nil)
  60. {
  61. // add a image(nil color) to _UIBarBackground make it clear
  62. setBackgroundImage(UIImage(), for: .default)
  63. backgroundView = UIView(frame: CGRect(x: 0, y: 0, width: Int(bounds.width), height: WRNavigationBar.navBarBottom()))
  64. backgroundView?.autoresizingMask = .flexibleWidth
  65. // _UIBarBackground is first subView for navigationBar
  66. subviews.first?.insertSubview(backgroundView ?? UIView(), at: 0)
  67. }
  68. backgroundView?.backgroundColor = color
  69. }
  70. // set _UIBarBackground alpha (_UIBarBackground subviews alpha <= _UIBarBackground alpha)
  71. fileprivate func wr_setBackgroundAlpha(alpha:CGFloat)
  72. {
  73. if let barBackgroundView = subviews.first
  74. {
  75. if #available(iOS 11.0, *)
  76. { // sometimes we can't change _UIBarBackground alpha
  77. for view in barBackgroundView.subviews {
  78. view.alpha = alpha
  79. }
  80. } else {
  81. barBackgroundView.alpha = alpha
  82. }
  83. }
  84. }
  85. // 设置导航栏所有BarButtonItem的透明度
  86. func wr_setBarButtonItemsAlpha(alpha:CGFloat, hasSystemBackIndicator:Bool)
  87. {
  88. for view in subviews
  89. {
  90. if (hasSystemBackIndicator == true)
  91. {
  92. // _UIBarBackground/_UINavigationBarBackground对应的view是系统导航栏,不需要改变其透明度
  93. if let _UIBarBackgroundClass = NSClassFromString("_UIBarBackground")
  94. {
  95. if view.isKind(of: _UIBarBackgroundClass) == false {
  96. view.alpha = alpha
  97. }
  98. }
  99. if let _UINavigationBarBackground = NSClassFromString("_UINavigationBarBackground")
  100. {
  101. if view.isKind(of: _UINavigationBarBackground) == false {
  102. view.alpha = alpha
  103. }
  104. }
  105. }
  106. else
  107. {
  108. // 这里如果不做判断的话,会显示 backIndicatorImage(系统返回按钮)
  109. if let _UINavigationBarBackIndicatorViewClass = NSClassFromString("_UINavigationBarBackIndicatorView"),
  110. view.isKind(of: _UINavigationBarBackIndicatorViewClass) == false
  111. {
  112. if let _UIBarBackgroundClass = NSClassFromString("_UIBarBackground")
  113. {
  114. if view.isKind(of: _UIBarBackgroundClass) == false {
  115. view.alpha = alpha
  116. }
  117. }
  118. if let _UINavigationBarBackground = NSClassFromString("_UINavigationBarBackground")
  119. {
  120. if view.isKind(of: _UINavigationBarBackground) == false {
  121. view.alpha = alpha
  122. }
  123. }
  124. }
  125. }
  126. }
  127. }
  128. /// 设置导航栏在垂直方向上平移多少距离
  129. func wr_setTranslationY(translationY:CGFloat)
  130. {
  131. transform = CGAffineTransform.init(translationX: 0, y: translationY)
  132. }
  133. func wr_getTranslationY() -> CGFloat
  134. {
  135. return transform.ty
  136. }
  137. // call swizzling methods active 主动调用交换方法
  138. private static let onceToken = UUID().uuidString
  139. public static func wrAwake()
  140. {
  141. DispatchQueue.once(token: onceToken)
  142. {
  143. let needSwizzleSelectorArr = [
  144. #selector(setter: titleTextAttributes)
  145. ]
  146. for selector in needSwizzleSelectorArr {
  147. let str = ("wr_" + selector.description)
  148. if let originalMethod = class_getInstanceMethod(self, selector),
  149. let swizzledMethod = class_getInstanceMethod(self, Selector(str)) {
  150. method_exchangeImplementations(originalMethod, swizzledMethod)
  151. }
  152. }
  153. }
  154. }
  155. //==========================================================================
  156. // MARK: swizzling pop
  157. //==========================================================================
  158. @objc func wr_setTitleTextAttributes(_ newTitleTextAttributes:[String : Any]?)
  159. {
  160. guard var attributes = newTitleTextAttributes else {
  161. return
  162. }
  163. guard let originTitleTextAttributes = titleTextAttributes else {
  164. wr_setTitleTextAttributes(attributes)
  165. return
  166. }
  167. var titleColor:UIColor?
  168. for attribute in originTitleTextAttributes {
  169. if attribute.key == NSAttributedString.Key.foregroundColor {
  170. titleColor = attribute.value as? UIColor
  171. break
  172. }
  173. }
  174. guard let originTitleColor = titleColor else {
  175. wr_setTitleTextAttributes(attributes)
  176. return
  177. }
  178. if attributes[NSAttributedString.Key.foregroundColor.rawValue] == nil {
  179. attributes.updateValue(originTitleColor, forKey: NSAttributedString.Key.foregroundColor.rawValue)
  180. }
  181. wr_setTitleTextAttributes(attributes)
  182. }
  183. }
  184. //==========================================================================
  185. // MARK: - UINavigationController
  186. //==========================================================================
  187. extension UINavigationController: WRFatherAwakeProtocol
  188. {
  189. override open var preferredStatusBarStyle: UIStatusBarStyle {
  190. return topViewController?.statusBarStyle ?? WRNavigationBar.defaultStatusBarStyle
  191. }
  192. fileprivate func setNeedsNavigationBarUpdate(backgroundImage: UIImage)
  193. {
  194. navigationBar.wr_setBackgroundImage(image: backgroundImage)
  195. }
  196. fileprivate func setNeedsNavigationBarUpdate(barTintColor: UIColor)
  197. {
  198. navigationBar.wr_setBackgroundColor(color: barTintColor)
  199. }
  200. fileprivate func setNeedsNavigationBarUpdate(barBackgroundAlpha: CGFloat)
  201. {
  202. navigationBar.wr_setBackgroundAlpha(alpha: barBackgroundAlpha)
  203. }
  204. fileprivate func setNeedsNavigationBarUpdate(tintColor: UIColor) {
  205. navigationBar.tintColor = tintColor
  206. }
  207. fileprivate func setNeedsNavigationBarUpdate(hideShadowImage: Bool)
  208. {
  209. navigationBar.shadowImage = (hideShadowImage == true) ? UIImage() : nil
  210. }
  211. fileprivate func setNeedsNavigationBarUpdate(titleColor: UIColor)
  212. {
  213. guard let titleTextAttributes = navigationBar.titleTextAttributes else {
  214. navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor:titleColor]
  215. return
  216. }
  217. var newTitleTextAttributes = titleTextAttributes
  218. newTitleTextAttributes.updateValue(titleColor, forKey: NSAttributedString.Key.foregroundColor)
  219. navigationBar.titleTextAttributes = newTitleTextAttributes
  220. }
  221. fileprivate func updateNavigationBar(fromVC: UIViewController?, toVC: UIViewController?, progress: CGFloat)
  222. {
  223. // change navBarBarTintColor
  224. let fromBarTintColor = fromVC?.navBarBarTintColor ?? WRNavigationBar.defaultNavBarBarTintColor
  225. let toBarTintColor = toVC?.navBarBarTintColor ?? WRNavigationBar.defaultNavBarBarTintColor
  226. let newBarTintColor = WRNavigationBar.middleColor(fromColor: fromBarTintColor, toColor: toBarTintColor, percent: progress)
  227. setNeedsNavigationBarUpdate(barTintColor: newBarTintColor)
  228. // change navBarTintColor
  229. let fromTintColor = fromVC?.navBarTintColor ?? WRNavigationBar.defaultNavBarTintColor
  230. let toTintColor = toVC?.navBarTintColor ?? WRNavigationBar.defaultNavBarTintColor
  231. let newTintColor = WRNavigationBar.middleColor(fromColor: fromTintColor, toColor: toTintColor, percent: progress)
  232. setNeedsNavigationBarUpdate(tintColor: newTintColor)
  233. // change navBarTitleColor
  234. // let fromTitleColor = fromVC?.navBarTitleColor ?? WRNavigationBar.defaultNavBarTitleColor
  235. // let toTitleColor = toVC?.navBarTitleColor ?? WRNavigationBar.defaultNavBarTitleColor
  236. // let newTitleColor = WRNavigationBar.middleColor(fromColor: fromTitleColor, toColor: toTitleColor, percent: progress)
  237. // setNeedsNavigationBarUpdate(titleColor: newTitleColor)
  238. // change navBar _UIBarBackground alpha
  239. let fromBarBackgroundAlpha = fromVC?.navBarBackgroundAlpha ?? WRNavigationBar.defaultBackgroundAlpha
  240. let toBarBackgroundAlpha = toVC?.navBarBackgroundAlpha ?? WRNavigationBar.defaultBackgroundAlpha
  241. let newBarBackgroundAlpha = WRNavigationBar.middleAlpha(fromAlpha: fromBarBackgroundAlpha, toAlpha: toBarBackgroundAlpha, percent: progress)
  242. setNeedsNavigationBarUpdate(barBackgroundAlpha: newBarBackgroundAlpha)
  243. }
  244. // call swizzling methods active 主动调用交换方法
  245. private static let onceToken = UUID().uuidString
  246. public static func fatherAwake()
  247. {
  248. DispatchQueue.once(token: onceToken)
  249. {
  250. let needSwizzleSelectorArr = [
  251. NSSelectorFromString("_updateInteractiveTransition:"),
  252. #selector(popToViewController),
  253. #selector(popToRootViewController),
  254. #selector(pushViewController)
  255. ]
  256. for selector in needSwizzleSelectorArr {
  257. // _updateInteractiveTransition: => wr_updateInteractiveTransition:
  258. let str = ("wr_" + selector.description).replacingOccurrences(of: "__", with: "_")
  259. if let originalMethod = class_getInstanceMethod(self, selector),
  260. let swizzledMethod = class_getInstanceMethod(self, Selector(str)) {
  261. method_exchangeImplementations(originalMethod, swizzledMethod)
  262. }
  263. }
  264. }
  265. }
  266. //==========================================================================
  267. // MARK: swizzling pop
  268. //==========================================================================
  269. struct popProperties {
  270. fileprivate static let popDuration = 0.13
  271. fileprivate static var displayCount = 0
  272. fileprivate static var popProgress:CGFloat {
  273. let all:CGFloat = CGFloat(60.0 * popDuration)
  274. let current = min(all, CGFloat(displayCount))
  275. return current / all
  276. }
  277. }
  278. // swizzling system method: popToViewController
  279. // @objc func wr_popToViewController(_ viewController: UIViewController, animated: Bool) -> [UIViewController]?
  280. // {
  281. // setNeedsNavigationBarUpdate(titleColor: viewController.navBarTitleColor)
  282. // var displayLink:CADisplayLink? = CADisplayLink(target: self, selector: #selector(popNeedDisplay))
  283. // // UITrackingRunLoopMode: 界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
  284. // // NSRunLoopCommonModes contains kCFRunLoopDefaultMode and UITrackingRunLoopMode
  285. // displayLink?.add(to: RunLoop.main, forMode: RunLoop.Mode.common)
  286. // CATransaction.setCompletionBlock {
  287. // displayLink?.invalidate()
  288. // displayLink = nil
  289. // popProperties.displayCount = 0
  290. // }
  291. // CATransaction.setAnimationDuration(popProperties.popDuration)
  292. // CATransaction.begin()
  293. // let vcs = wr_popToViewController(viewController, animated: animated)
  294. // CATransaction.commit()
  295. // return vcs
  296. // }
  297. // swizzling system method: popToRootViewControllerAnimated
  298. @objc func wr_popToRootViewControllerAnimated(_ animated: Bool) -> [UIViewController]?
  299. {
  300. var displayLink:CADisplayLink? = CADisplayLink(target: self, selector: #selector(popNeedDisplay))
  301. displayLink?.add(to: RunLoop.main, forMode: RunLoop.Mode.common)
  302. CATransaction.setCompletionBlock {
  303. displayLink?.invalidate()
  304. displayLink = nil
  305. popProperties.displayCount = 0
  306. }
  307. CATransaction.setAnimationDuration(popProperties.popDuration)
  308. CATransaction.begin()
  309. let vcs = wr_popToRootViewControllerAnimated(animated)
  310. CATransaction.commit()
  311. return vcs;
  312. }
  313. // change navigationBar barTintColor smooth before pop to current VC finished
  314. @objc fileprivate func popNeedDisplay()
  315. {
  316. guard let topViewController = topViewController,
  317. let coordinator = topViewController.transitionCoordinator else {
  318. return
  319. }
  320. popProperties.displayCount += 1
  321. let popProgress = popProperties.popProgress
  322. // print("第\(popProperties.displayCount)次pop的进度:\(popProgress)")
  323. let fromVC = coordinator.viewController(forKey: .from)
  324. let toVC = coordinator.viewController(forKey: .to)
  325. updateNavigationBar(fromVC: fromVC, toVC: toVC, progress: popProgress)
  326. }
  327. //==========================================================================
  328. // MARK: swizzling push
  329. //==========================================================================
  330. struct pushProperties {
  331. fileprivate static let pushDuration = 0.13
  332. fileprivate static var displayCount = 0
  333. fileprivate static var pushProgress:CGFloat {
  334. let all:CGFloat = CGFloat(60.0 * pushDuration)
  335. let current = min(all, CGFloat(displayCount))
  336. return current / all
  337. }
  338. }
  339. // swizzling system method: pushViewController
  340. @objc func wr_pushViewController(_ viewController: UIViewController, animated: Bool)
  341. {
  342. var displayLink:CADisplayLink? = CADisplayLink(target: self, selector: #selector(pushNeedDisplay))
  343. displayLink?.add(to: RunLoop.main, forMode: RunLoop.Mode.common)
  344. CATransaction.setCompletionBlock {
  345. displayLink?.invalidate()
  346. displayLink = nil
  347. pushProperties.displayCount = 0
  348. viewController.pushToCurrentVCFinished = true
  349. };
  350. CATransaction.setAnimationDuration(pushProperties.pushDuration)
  351. CATransaction.begin()
  352. wr_pushViewController(viewController, animated: animated)
  353. CATransaction.commit()
  354. }
  355. // change navigationBar barTintColor smooth before push to current VC finished or before pop to current VC finished
  356. @objc fileprivate func pushNeedDisplay()
  357. {
  358. guard let topViewController = topViewController,
  359. let coordinator = topViewController.transitionCoordinator else {
  360. return
  361. }
  362. pushProperties.displayCount += 1
  363. let pushProgress = pushProperties.pushProgress
  364. // print("第\(pushProperties.displayCount)次push的进度:\(pushProgress)")
  365. let fromVC = coordinator.viewController(forKey: .from)
  366. let toVC = coordinator.viewController(forKey: .to)
  367. updateNavigationBar(fromVC: fromVC, toVC: toVC, progress: pushProgress)
  368. }
  369. }
  370. //==========================================================================
  371. // MARK: - deal the gesture of return
  372. //==========================================================================
  373. extension UINavigationController: UINavigationBarDelegate
  374. {
  375. // public func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool
  376. // {
  377. // if let topVC = topViewController,
  378. // let coor = topVC.transitionCoordinator, coor.initiallyInteractive {
  379. // if #available(iOS 10.0, *) {
  380. // coor.notifyWhenInteractionChanges({ (context) in
  381. // self.dealInteractionChanges(context)
  382. // })
  383. // } else {
  384. // coor.notifyWhenInteractionEnds({ (context) in
  385. // self.dealInteractionChanges(context)
  386. // })
  387. // }
  388. // return true
  389. // }
  390. //
  391. // let itemCount = navigationBar.items?.count ?? 0
  392. // let n = viewControllers.count >= itemCount ? 2 : 1
  393. // let popToVC = viewControllers[viewControllers.count - n]
  394. //
  395. // popToViewController(popToVC, animated: true)
  396. // return true
  397. // }
  398. // deal the gesture of return break off
  399. private func dealInteractionChanges(_ context: UIViewControllerTransitionCoordinatorContext)
  400. {
  401. let animations: (UITransitionContextViewControllerKey) -> () = {
  402. let curColor = context.viewController(forKey: $0)?.navBarBarTintColor ?? WRNavigationBar.defaultNavBarBarTintColor
  403. let curAlpha = context.viewController(forKey: $0)?.navBarBackgroundAlpha ?? WRNavigationBar.defaultBackgroundAlpha
  404. self.setNeedsNavigationBarUpdate(barTintColor: curColor)
  405. self.setNeedsNavigationBarUpdate(barBackgroundAlpha: curAlpha)
  406. }
  407. // after that, cancel the gesture of return
  408. if context.isCancelled
  409. {
  410. let cancelDuration: TimeInterval = context.transitionDuration * Double(context.percentComplete)
  411. UIView.animate(withDuration: cancelDuration) {
  412. animations(.from)
  413. }
  414. }
  415. else
  416. {
  417. // after that, finish the gesture of return
  418. let finishDuration: TimeInterval = context.transitionDuration * Double(1 - context.percentComplete)
  419. UIView.animate(withDuration: finishDuration) {
  420. animations(.to)
  421. }
  422. }
  423. }
  424. // swizzling system method: _updateInteractiveTransition
  425. @objc func wr_updateInteractiveTransition(_ percentComplete: CGFloat)
  426. {
  427. guard let topViewController = topViewController,
  428. let coordinator = topViewController.transitionCoordinator else {
  429. wr_updateInteractiveTransition(percentComplete)
  430. return
  431. }
  432. let fromVC = coordinator.viewController(forKey: .from)
  433. let toVC = coordinator.viewController(forKey: .to)
  434. updateNavigationBar(fromVC: fromVC, toVC: toVC, progress: percentComplete)
  435. wr_updateInteractiveTransition(percentComplete)
  436. }
  437. }
  438. //=============================================================================
  439. // MARK: - store navigationBar barTintColor and tintColor every viewController
  440. //=============================================================================
  441. @objc extension UIViewController: WRAwakeProtocol
  442. {
  443. fileprivate struct AssociatedKeys
  444. {
  445. static var pushToCurrentVCFinished: Bool = false
  446. static var pushToNextVCFinished:Bool = false
  447. static var navBarBackgroundImage: UIImage = UIImage()
  448. static var navBarBarTintColor: UIColor = WRNavigationBar.defaultNavBarBarTintColor
  449. static var navBarBackgroundAlpha:CGFloat = 1.0
  450. static var navBarTintColor: UIColor = WRNavigationBar.defaultNavBarTintColor
  451. static var navBarTitleColor: UIColor = WRNavigationBar.defaultNavBarTitleColor
  452. static var statusBarStyle: UIStatusBarStyle = UIStatusBarStyle.default
  453. static var navBarShadowImageHidden: Bool = false
  454. static var customNavBar: UINavigationBar = UINavigationBar()
  455. }
  456. // navigationBar barTintColor can not change by currentVC before fromVC push to currentVC finished
  457. fileprivate var pushToCurrentVCFinished:Bool {
  458. get {
  459. guard let isFinished = objc_getAssociatedObject(self, &AssociatedKeys.pushToCurrentVCFinished) as? Bool else {
  460. return false
  461. }
  462. return isFinished
  463. }
  464. set {
  465. objc_setAssociatedObject(self, &AssociatedKeys.pushToCurrentVCFinished, newValue, .OBJC_ASSOCIATION_ASSIGN)
  466. }
  467. }
  468. // navigationBar barTintColor can not change by currentVC when currentVC push to nextVC finished
  469. fileprivate var pushToNextVCFinished:Bool {
  470. get {
  471. guard let isFinished = objc_getAssociatedObject(self, &AssociatedKeys.pushToNextVCFinished) as? Bool else {
  472. return false
  473. }
  474. return isFinished
  475. }
  476. set {
  477. objc_setAssociatedObject(self, &AssociatedKeys.pushToNextVCFinished, newValue, .OBJC_ASSOCIATION_ASSIGN)
  478. }
  479. }
  480. // you can set navigationBar backgroundImage
  481. var navBarBackgroundImage: UIImage?
  482. {
  483. get {
  484. guard let bgImage = objc_getAssociatedObject(self, &AssociatedKeys.navBarBackgroundImage) as? UIImage else {
  485. return WRNavigationBar.defaultNavBarBackgroundImage
  486. }
  487. return bgImage
  488. }
  489. // set {
  490. // if customNavBar.isKind(of: UINavigationBar.self) {
  491. // let navBar = customNavBar as! UINavigationBar
  492. // navBar.wr_setBackgroundImage(image: newValue!)
  493. // }
  494. // else {
  495. // objc_setAssociatedObject(self, &AssociatedKeys.navBarBackgroundImage, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  496. // }
  497. // }
  498. }
  499. // navigationBar barTintColor
  500. var navBarBarTintColor: UIColor {
  501. get {
  502. guard let barTintColor = objc_getAssociatedObject(self, &AssociatedKeys.navBarBarTintColor) as? UIColor else {
  503. return WRNavigationBar.defaultNavBarBarTintColor
  504. }
  505. return barTintColor
  506. }
  507. set {
  508. objc_setAssociatedObject(self, &AssociatedKeys.navBarBarTintColor, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  509. if customNavBar.isKind(of: UINavigationBar.self) {
  510. // let navBar = customNavBar as! UINavigationBar
  511. // navBar.wr_setBackgroundColor(color: newValue)
  512. }
  513. else {
  514. if canUpdateNavBarBarTintColorOrBackgroundAlpha == true {
  515. navigationController?.setNeedsNavigationBarUpdate(barTintColor: newValue)
  516. }
  517. }
  518. }
  519. }
  520. // navigationBar _UIBarBackground alpha
  521. var navBarBackgroundAlpha:CGFloat {
  522. get {
  523. guard let barBackgroundAlpha = objc_getAssociatedObject(self, &AssociatedKeys.navBarBackgroundAlpha) as? CGFloat else {
  524. return 1.0
  525. }
  526. return barBackgroundAlpha
  527. }
  528. set {
  529. objc_setAssociatedObject(self, &AssociatedKeys.navBarBackgroundAlpha, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  530. if customNavBar.isKind(of: UINavigationBar.self) {
  531. // let navBar = customNavBar as! UINavigationBar
  532. // navBar.wr_setBackgroundAlpha(alpha: newValue)
  533. }
  534. else {
  535. if canUpdateNavBarBarTintColorOrBackgroundAlpha == true {
  536. navigationController?.setNeedsNavigationBarUpdate(barBackgroundAlpha: newValue)
  537. }
  538. }
  539. }
  540. }
  541. private var canUpdateNavBarBarTintColorOrBackgroundAlpha:Bool {
  542. get {
  543. let isRootViewController = self.navigationController?.viewControllers.first == self
  544. if (pushToCurrentVCFinished == true || isRootViewController == true) && pushToNextVCFinished == false {
  545. return true
  546. } else {
  547. return false
  548. }
  549. }
  550. }
  551. // navigationBar tintColor
  552. var navBarTintColor: UIColor {
  553. get {
  554. guard let tintColor = objc_getAssociatedObject(self, &AssociatedKeys.navBarTintColor) as? UIColor else {
  555. return WRNavigationBar.defaultNavBarTintColor
  556. }
  557. return tintColor
  558. }
  559. set {
  560. objc_setAssociatedObject(self, &AssociatedKeys.navBarTintColor, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  561. if customNavBar.isKind(of: UINavigationBar.self) {
  562. // let navBar = customNavBar as! UINavigationBar
  563. // navBar.tintColor = newValue
  564. }
  565. else
  566. {
  567. if pushToNextVCFinished == false {
  568. navigationController?.setNeedsNavigationBarUpdate(tintColor: newValue)
  569. }
  570. }
  571. }
  572. }
  573. // navigationBar titleColor
  574. var navBarTitleColor: UIColor {
  575. get {
  576. guard let titleColor = objc_getAssociatedObject(self, &AssociatedKeys.navBarTitleColor) as? UIColor else {
  577. return WRNavigationBar.defaultNavBarTitleColor
  578. }
  579. return titleColor
  580. }
  581. set {
  582. objc_setAssociatedObject(self, &AssociatedKeys.navBarTitleColor, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  583. if customNavBar.isKind(of: UINavigationBar.self) {
  584. // let navBar = customNavBar as! UINavigationBar
  585. // navBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor:newValue]
  586. }
  587. else
  588. {
  589. if pushToNextVCFinished == false {
  590. navigationController?.setNeedsNavigationBarUpdate(titleColor: newValue)
  591. }
  592. }
  593. }
  594. }
  595. // statusBarStyle
  596. var statusBarStyle: UIStatusBarStyle {
  597. get {
  598. guard let style = objc_getAssociatedObject(self, &AssociatedKeys.statusBarStyle) as? UIStatusBarStyle else {
  599. return WRNavigationBar.defaultStatusBarStyle
  600. }
  601. return style
  602. }
  603. set {
  604. objc_setAssociatedObject(self, &AssociatedKeys.statusBarStyle, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  605. setNeedsStatusBarAppearanceUpdate()
  606. }
  607. }
  608. // if you want shadowImage hidden,you can via hideShadowImage = true
  609. var navBarShadowImageHidden:Bool {
  610. get {
  611. guard let isHidden = objc_getAssociatedObject(self, &AssociatedKeys.navBarShadowImageHidden) as? Bool else {
  612. return WRNavigationBar.defaultShadowImageHidden
  613. }
  614. return isHidden
  615. }
  616. set {
  617. objc_setAssociatedObject(self, &AssociatedKeys.navBarShadowImageHidden, newValue, .OBJC_ASSOCIATION_ASSIGN)
  618. navigationController?.setNeedsNavigationBarUpdate(hideShadowImage: newValue)
  619. }
  620. }
  621. // custom navigationBar
  622. var customNavBar: UIView {
  623. get {
  624. guard let navBar = objc_getAssociatedObject(self, &AssociatedKeys.customNavBar) as? UINavigationBar else {
  625. return UIView()
  626. }
  627. return navBar
  628. }
  629. set {
  630. objc_setAssociatedObject(self, &AssociatedKeys.customNavBar, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  631. }
  632. }
  633. // swizzling two system methods: viewWillAppear(_:) and viewWillDisappear(_:)
  634. private static let onceToken = UUID().uuidString
  635. @objc public static func wrAwake()
  636. {
  637. DispatchQueue.once(token: onceToken)
  638. {
  639. let needSwizzleSelectors = [
  640. #selector(viewWillAppear(_:)),
  641. #selector(viewWillDisappear(_:)),
  642. #selector(viewDidAppear(_:))
  643. ]
  644. for selector in needSwizzleSelectors
  645. {
  646. let newSelectorStr = "wr_" + selector.description
  647. if let originalMethod = class_getInstanceMethod(self, selector),
  648. let swizzledMethod = class_getInstanceMethod(self, Selector(newSelectorStr)) {
  649. method_exchangeImplementations(originalMethod, swizzledMethod)
  650. }
  651. }
  652. }
  653. }
  654. @objc func wr_viewWillAppear(_ animated: Bool)
  655. {
  656. if canUpdateNavigationBar() == true {
  657. pushToNextVCFinished = false
  658. navigationController?.setNeedsNavigationBarUpdate(tintColor: navBarTintColor)
  659. navigationController?.setNeedsNavigationBarUpdate(titleColor: navBarTitleColor)
  660. }
  661. wr_viewWillAppear(animated)
  662. }
  663. @objc func wr_viewWillDisappear(_ animated: Bool)
  664. {
  665. if canUpdateNavigationBar() == true {
  666. pushToNextVCFinished = true
  667. }
  668. wr_viewWillDisappear(animated)
  669. }
  670. @objc func wr_viewDidAppear(_ animated: Bool)
  671. {
  672. if self.navigationController?.viewControllers.first != self {
  673. self.pushToCurrentVCFinished = true
  674. }
  675. if canUpdateNavigationBar() == true
  676. {
  677. if let navBarBgImage = navBarBackgroundImage {
  678. navigationController?.setNeedsNavigationBarUpdate(backgroundImage: navBarBgImage)
  679. } else {
  680. navigationController?.setNeedsNavigationBarUpdate(barTintColor: navBarBarTintColor)
  681. }
  682. navigationController?.setNeedsNavigationBarUpdate(barBackgroundAlpha: navBarBackgroundAlpha)
  683. navigationController?.setNeedsNavigationBarUpdate(tintColor: navBarTintColor)
  684. navigationController?.setNeedsNavigationBarUpdate(titleColor: navBarTitleColor)
  685. navigationController?.setNeedsNavigationBarUpdate(hideShadowImage: navBarShadowImageHidden)
  686. }
  687. wr_viewDidAppear(animated)
  688. }
  689. func canUpdateNavigationBar() -> Bool
  690. {
  691. let viewFrame = view.frame
  692. let maxFrame = UIScreen.main.bounds
  693. let middleFrame = CGRect(x: 0, y: WRNavigationBar.navBarBottom(), width: WRNavigationBar.screenWidth(), height: WRNavigationBar.screenHeight()-WRNavigationBar.navBarBottom())
  694. let minFrame = CGRect(x: 0, y: WRNavigationBar.navBarBottom(), width: WRNavigationBar.screenWidth(), height: WRNavigationBar.screenHeight()-WRNavigationBar.navBarBottom()-WRNavigationBar.tabBarHeight())
  695. // 蝙蝠🦇
  696. let isBat = viewFrame.equalTo(maxFrame) || viewFrame.equalTo(middleFrame) || viewFrame.equalTo(minFrame)
  697. if self.navigationController != nil && isBat == true {
  698. return true
  699. } else {
  700. return false
  701. }
  702. }
  703. }
  704. //====================================================================================
  705. // MARK: - Swizzling会改变全局状态,所以用 DispatchQueue.once 来确保无论多少线程都只会被执行一次
  706. //====================================================================================
  707. extension DispatchQueue {
  708. private static var onceTracker = [String]()
  709. //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.
  710. public class func once(token: String, block: () -> Void)
  711. { // 保证被 objc_sync_enter 和 objc_sync_exit 包裹的代码可以有序同步地执行
  712. objc_sync_enter(self)
  713. defer { // 作用域结束后执行defer中的代码
  714. objc_sync_exit(self)
  715. }
  716. if onceTracker.contains(token) {
  717. return
  718. }
  719. onceTracker.append(token)
  720. block()
  721. }
  722. }
  723. //===========================================================================================
  724. // MARK: - default navigationBar barTintColor、tintColor and statusBarStyle YOU CAN CHANGE!!!
  725. //===========================================================================================
  726. class WRNavigationBar
  727. {
  728. fileprivate struct AssociatedKeys
  729. { // default is system attributes
  730. static var defNavBarBarTintColor: UIColor = UIColor.white
  731. static var defNavBarBackgroundImage: UIImage = UIImage()
  732. static var defNavBarTintColor: UIColor = UIColor(red: 0, green: 0.478431, blue: 1, alpha: 1.0)
  733. static var defNavBarTitleColor: UIColor = UIColor.black
  734. static var defStatusBarStyle: UIStatusBarStyle = UIStatusBarStyle.default
  735. static var defShadowImageHidden: Bool = false
  736. }
  737. class var defaultNavBarBarTintColor: UIColor {
  738. get {
  739. guard let def = objc_getAssociatedObject(self, &AssociatedKeys.defNavBarBarTintColor) as? UIColor else {
  740. return AssociatedKeys.defNavBarBarTintColor
  741. }
  742. return def
  743. }
  744. set {
  745. objc_setAssociatedObject(self, &AssociatedKeys.defNavBarBarTintColor, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  746. }
  747. }
  748. class var defaultNavBarBackgroundImage: UIImage? {
  749. get {
  750. guard let def = objc_getAssociatedObject(self, &AssociatedKeys.defNavBarBackgroundImage) as? UIImage else {
  751. return nil
  752. }
  753. return def
  754. }
  755. set {
  756. objc_setAssociatedObject(self, &AssociatedKeys.defNavBarBackgroundImage, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  757. }
  758. }
  759. class var defaultNavBarTintColor: UIColor {
  760. get {
  761. guard let def = objc_getAssociatedObject(self, &AssociatedKeys.defNavBarTintColor) as? UIColor else {
  762. return AssociatedKeys.defNavBarTintColor
  763. }
  764. return def
  765. }
  766. set {
  767. objc_setAssociatedObject(self, &AssociatedKeys.defNavBarTintColor, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  768. }
  769. }
  770. class var defaultNavBarTitleColor: UIColor {
  771. get {
  772. guard let def = objc_getAssociatedObject(self, &AssociatedKeys.defNavBarTitleColor) as? UIColor else {
  773. return AssociatedKeys.defNavBarTitleColor
  774. }
  775. return def
  776. }
  777. set {
  778. objc_setAssociatedObject(self, &AssociatedKeys.defNavBarTitleColor, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  779. }
  780. }
  781. class var defaultStatusBarStyle: UIStatusBarStyle {
  782. get {
  783. guard let def = objc_getAssociatedObject(self, &AssociatedKeys.defStatusBarStyle) as? UIStatusBarStyle else {
  784. return .default
  785. }
  786. return def
  787. }
  788. set {
  789. objc_setAssociatedObject(self, &AssociatedKeys.defStatusBarStyle, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
  790. }
  791. }
  792. class var defaultShadowImageHidden: Bool {
  793. get {
  794. guard let def = objc_getAssociatedObject(self, &AssociatedKeys.defShadowImageHidden) as? Bool else {
  795. return false
  796. }
  797. return def
  798. }
  799. set {
  800. objc_setAssociatedObject(self, &AssociatedKeys.defShadowImageHidden, newValue, .OBJC_ASSOCIATION_ASSIGN)
  801. }
  802. }
  803. class var defaultBackgroundAlpha: CGFloat {
  804. get {
  805. return 1.0
  806. }
  807. }
  808. // Calculate the middle Color with translation percent
  809. class fileprivate func middleColor(fromColor: UIColor, toColor: UIColor, percent: CGFloat) -> UIColor
  810. {
  811. // get current color RGBA
  812. var fromRed: CGFloat = 0
  813. var fromGreen: CGFloat = 0
  814. var fromBlue: CGFloat = 0
  815. var fromAlpha: CGFloat = 0
  816. fromColor.getRed(&fromRed, green: &fromGreen, blue: &fromBlue, alpha: &fromAlpha)
  817. // get to color RGBA
  818. var toRed: CGFloat = 0
  819. var toGreen: CGFloat = 0
  820. var toBlue: CGFloat = 0
  821. var toAlpha: CGFloat = 0
  822. toColor.getRed(&toRed, green: &toGreen, blue: &toBlue, alpha: &toAlpha)
  823. // calculate middle color RGBA
  824. let newRed = fromRed + (toRed - fromRed) * percent
  825. let newGreen = fromGreen + (toGreen - fromGreen) * percent
  826. let newBlue = fromBlue + (toBlue - fromBlue) * percent
  827. let newAlpha = fromAlpha + (toAlpha - fromAlpha) * percent
  828. return UIColor(red: newRed, green: newGreen, blue: newBlue, alpha: newAlpha)
  829. }
  830. // Calculate the middle alpha
  831. class fileprivate func middleAlpha(fromAlpha: CGFloat, toAlpha: CGFloat, percent: CGFloat) -> CGFloat
  832. {
  833. let newAlpha = fromAlpha + (toAlpha - fromAlpha) * percent
  834. return newAlpha
  835. }
  836. }
  837. extension WRNavigationBar
  838. {
  839. class func navBarBottom() -> Int {
  840. return kIsIphoneX ? 88 : 64;
  841. }
  842. class func tabBarHeight() -> Int {
  843. return kIsIphoneX ? 83 : 49;
  844. }
  845. class func screenWidth() -> Int {
  846. return Int(UIScreen.main.bounds.size.width)
  847. }
  848. class func screenHeight() -> Int {
  849. return Int(UIScreen.main.bounds.size.height)
  850. }
  851. }
  852. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  853. // 1. 定义 WRAwakeProtocol 协议
  854. public protocol WRAwakeProtocol: class {
  855. static func wrAwake()
  856. }
  857. public protocol WRFatherAwakeProtocol: class
  858. { // 1.1 定义 WRFatherAwakeProtocol ()
  859. static func fatherAwake()
  860. }
  861. class NothingToSeeHere
  862. {
  863. static func harmlessFunction(){
  864. // let typeCount = Int(objc_getClassList(nil, 0))
  865. // let types = UnsafeMutablePointer<AnyClass?>.allocate(capacity: typeCount)
  866. // let autoreleaseintTypes = AutoreleasingUnsafeMutablePointer<AnyClass>(types)
  867. // objc_getClassList(autoreleaseintTypes, Int32(typeCount)) //获取所有的类
  868. // for index in 0 ..< typeCount {
  869. // (types[index] as? WRAwakeProtocol.Type)?.wrAwake() //如果该类实现了SelfAware协议,那么调用 awake 方法
  870. // (types[index] as? WRFatherAwakeProtocol.Type)?.fatherAwake()
  871. // }
  872. // types.deallocate(capacity: typeCount)
  873. UINavigationBar.wrAwake()
  874. UIViewController.wrAwake()
  875. UINavigationController.fatherAwake()
  876. }
  877. }
  878. // 2. 让APP启动时只执行一次 harmlessFunction 方法
  879. extension UIApplication
  880. {
  881. private static let runOnce:Void = { //使用静态属性以保证只调用一次(该属性是个方法)
  882. NothingToSeeHere.harmlessFunction()
  883. }()
  884. open override var next: UIResponder?{ //重写next属性
  885. UIApplication.runOnce
  886. return super.next
  887. }
  888. }
  889. // 3. 自定义类实现 WRAwakeProtocol 协议,重写 wrAwake 方法
  890. // 自定义类实现 WRFatherAwakeProtocol 协议,重写 fatherAwake 方法