开发中,我们时常会遇到导航栏颜色渐变,导航栏移动等功能;只需要复制以下系统和自定义导航栏代码到你新建的文件中,然后仿照试图控制器所举的例子使用即可,简单易用,别忘记在单例里面实现以下方法用以设置导航栏默认属性
//设置默认属性
- (void)setNavBarAppearence
{
MainNavBarColor = [UIColor colorWithRed:0/255.0 green:175/255.0 blue:240/255.0 alpha:1];
MainViewColor = [UIColor colorWithRed:126/255.0 green:126/255.0 blue:126/255.0 alpha:1];
// 设置导航栏默认的背景颜色
[WRNavigationBar wr_setDefaultNavBarBarTintColor:MainNavBarColor];
// 设置导航栏所有按钮的默认颜色
[WRNavigationBar wr_setDefaultNavBarTintColor:[UIColor whiteColor]];
// 设置导航栏标题默认颜色
[WRNavigationBar wr_setDefaultNavBarTitleColor:[UIColor whiteColor]];
// 统一设置状态栏样式
[WRNavigationBar wr_setDefaultStatusBarStyle:UIStatusBarStyleLightContent];
// 如果需要设置导航栏底部分割线隐藏,可以在这里统一设置
[WRNavigationBar wr_setDefaultNavBarShadowImageHidden:YES];
}
这里可以实现微博,QQ,知乎,拉勾等各种导航栏效果,实现简单,封装后只需要实现
- (void)scrollViewDidScroll:(UIScrollView *)scrollView即可
下面直接上代码:
首先封装系统导航栏:
.h文件
#import
@class WRCustomNavigationBar;
@interface WRNavigationBar : UIView
+ (BOOL)isIphoneX;
+ (int)navBarBottom;
+ (int)tabBarHeight;
+ (int)screenWidth;
+ (int)screenHeight;
@end
#pragma mark - Default
@interface WRNavigationBar (WRDefault)
/*
导航栏默认属性均可以使用以下方法设置,在AppDelegate里面设置,初始化导航栏的时候设置即可
*/
+ (void)wr_setDefaultNavBarBarTintColor:(UIColor *)color;
+ (void)wr_setDefaultNavBarTintColor:(UIColor *)color;
+ (void)wr_setDefaultNavBarTitleColor:(UIColor *)color;
+ (void)wr_setDefaultStatusBarStyle:(UIStatusBarStyle)style;
+ (void)wr_setDefaultNavBarShadowImageHidden:(BOOL)hidden;
@end
/**
* 赵发生
* ============================================================================
* * 版权所有 2017-2027 赵发生,并保留所有权利。
* ----------------------------------------------------------------------------
* 这不是一个自由软件!您只能在不用于商业目的的前提下对程序代码进行修改和使用 .
* 不允许对程序代码以任何形式任何目的的再发布。
* ============================================================================
* $Author: 赵发生 17/12/22
* $description: 导航栏封装
*/
#pragma mark - UINavigationBar
@interface UINavigationBar (WRAddition) <UINavigationBarDelegate>
/** 设置导航栏所有BarButtonItem的透明度 */
- (void)wr_setBarButtonItemsAlpha:(CGFloat)alpha hasSystemBackIndicator:(BOOL)hasSystemBackIndicator;
/** 设置导航栏在垂直方向上平移多少距离 */
- (void)wr_setTranslationY:(CGFloat)translationY;
/** 获取当前导航栏在垂直方向上偏移了多少 */
- (CGFloat)wr_getTranslationY;
@end
#pragma mark - UIViewController
@interface UIViewController (WRAddition)
- (UIImage *)wr_navBarBackgroundImage;
/**
设置导航栏颜色
@param color 色值
*/
- (void)wr_setNavBarBarTintColor:(UIColor *)color;
- (UIColor *)wr_navBarBarTintColor;
/**
设置导航栏背景透明度
@param alpha 透明度
*/
- (void)wr_setNavBarBackgroundAlpha:(CGFloat)alpha;
- (CGFloat)wr_navBarBackgroundAlpha;
/**
设置导航栏按钮和标题颜色
@param color 色值
*/
- (void)wr_setNavBarTintColor:(UIColor *)color;
- (UIColor *)wr_navBarTintColor;
/**
设置导航栏中间标题颜色
@param color 色值
*/
- (void)wr_setNavBarTitleColor:(UIColor *)color;
- (UIColor *)wr_navBarTitleColor;
- (void)wr_setStatusBarStyle:(UIStatusBarStyle)style;
- (UIStatusBarStyle)wr_statusBarStyle;
- (void)wr_setNavBarShadowImageHidden:(BOOL)hidden;
- (BOOL)wr_navBarShadowImageHidden;
@end
#import "WRNavigationBar.h"
#import
@implementation WRNavigationBar
+ (BOOL)isIphoneX
{
if (CGRectEqualToRect([UIScreen mainScreen].bounds,CGRectMake(0, 0, 375, 812))) {
return YES;
} else {
return NO;
}
}
+ (int)navBarBottom {
return [self isIphoneX] ? 88 : 64;
}
+ (int)tabBarHeight {
return [self isIphoneX] ? 83 : 49;
}
+ (int)screenWidth {
return [UIScreen mainScreen].bounds.size.width;
}
+ (int)screenHeight {
return [UIScreen mainScreen].bounds.size.height;
}
@end
//===============================================================================================
#pragma mark - default navigationBar barTintColor、tintColor and statusBarStyle YOU CAN CHANGE!!!
//===============================================================================================
@implementation WRNavigationBar (WRDefault)
static char kWRDefaultNavBarBarTintColorKey;
static char kWRDefaultNavBarBackgroundImageKey;
static char kWRDefaultNavBarTintColorKey;
static char kWRDefaultNavBarTitleColorKey;
static char kWRDefaultStatusBarStyleKey;
static char kWRDefaultNavBarShadowImageHiddenKey;
+ (UIColor *)defaultNavBarBarTintColor
{
UIColor *color = (UIColor *)objc_getAssociatedObject(self, &kWRDefaultNavBarBarTintColorKey);
return (color != nil) ? color : [UIColor whiteColor];
}
+ (void)wr_setDefaultNavBarBarTintColor:(UIColor *)color {
objc_setAssociatedObject(self, &kWRDefaultNavBarBarTintColorKey, color, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
+ (UIImage *)defaultNavBarBackgroundImage
{
UIImage *image = (UIImage *)objc_getAssociatedObject(self, &kWRDefaultNavBarBackgroundImageKey);
return image;
}
+ (void)wr_setDefaultNavBarBackgroundImage:(UIImage *)image {
objc_setAssociatedObject(self, &kWRDefaultNavBarBackgroundImageKey, image, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
+ (UIColor *)defaultNavBarTintColor {
UIColor *color = (UIColor *)objc_getAssociatedObject(self, &kWRDefaultNavBarTintColorKey);
return (color != nil) ? color : [UIColor colorWithRed:0 green:0.478431 blue:1 alpha:1.0];
}
+ (void)wr_setDefaultNavBarTintColor:(UIColor *)color {
objc_setAssociatedObject(self, &kWRDefaultNavBarTintColorKey, color, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
+ (UIColor *)defaultNavBarTitleColor
{
UIColor *color = (UIColor *)objc_getAssociatedObject(self, &kWRDefaultNavBarTitleColorKey);
return (color != nil) ? color : [UIColor blackColor];
}
+ (void)wr_setDefaultNavBarTitleColor:(UIColor *)color {
objc_setAssociatedObject(self, &kWRDefaultNavBarTitleColorKey, color, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
+ (UIStatusBarStyle)defaultStatusBarStyle
{
id style = objc_getAssociatedObject(self, &kWRDefaultStatusBarStyleKey);
return (style != nil) ? [style integerValue] : UIStatusBarStyleDefault;
}
+ (void)wr_setDefaultStatusBarStyle:(UIStatusBarStyle)style {
objc_setAssociatedObject(self, &kWRDefaultStatusBarStyleKey, @(style), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
+ (BOOL)defaultNavBarShadowImageHidden {
id hidden = objc_getAssociatedObject(self, &kWRDefaultNavBarShadowImageHiddenKey);
return (hidden != nil) ? [hidden boolValue] : NO;
}
+ (void)wr_setDefaultNavBarShadowImageHidden:(BOOL)hidden {
objc_setAssociatedObject(self, &kWRDefaultNavBarShadowImageHiddenKey, @(hidden), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
+ (CGFloat)defaultNavBarBackgroundAlpha {
return 1.0;
}
+ (UIColor *)middleColor:(UIColor *)fromColor toColor:(UIColor *)toColor percent:(CGFloat)percent
{
CGFloat fromRed = 0;
CGFloat fromGreen = 0;
CGFloat fromBlue = 0;
CGFloat fromAlpha = 0;
[fromColor getRed:&fromRed green:&fromGreen blue:&fromBlue alpha:&fromAlpha];
CGFloat toRed = 0;
CGFloat toGreen = 0;
CGFloat toBlue = 0;
CGFloat toAlpha = 0;
[toColor getRed:&toRed green:&toGreen blue:&toBlue alpha:&toAlpha];
CGFloat newRed = fromRed + (toRed - fromRed) * percent;
CGFloat newGreen = fromGreen + (toGreen - fromGreen) * percent;
CGFloat newBlue = fromBlue + (toBlue - fromBlue) * percent;
CGFloat newAlpha = fromAlpha + (toAlpha - fromAlpha) * percent;
return [UIColor colorWithRed:newRed green:newGreen blue:newBlue alpha:newAlpha];
}
+ (CGFloat)middleAlpha:(CGFloat)fromAlpha toAlpha:(CGFloat)toAlpha percent:(CGFloat)percent {
return fromAlpha + (toAlpha - fromAlpha) * percent;
}
@end
//==========================================================================
#pragma mark - UINavigationBar
//==========================================================================
@implementation UINavigationBar (WRAddition)
static char kWRBackgroundViewKey;
static char kWRBackgroundImageViewKey;
static char kWRBackgroundImageKey;
- (UIView *)backgroundView {
return (UIView *)objc_getAssociatedObject(self, &kWRBackgroundViewKey);
}
- (void)setBackgroundView:(UIView *)backgroundView {
objc_setAssociatedObject(self, &kWRBackgroundViewKey, backgroundView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIImageView *)backgroundImageView {
return (UIImageView *)objc_getAssociatedObject(self, &kWRBackgroundImageViewKey);
}
- (void)setBackgroundImageView:(UIImageView *)bgImageView {
objc_setAssociatedObject(self, &kWRBackgroundImageViewKey, bgImageView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (UIImage *)backgroundImage {
return (UIImage *)objc_getAssociatedObject(self, &kWRBackgroundImageKey);
}
- (void)setBackgroundImage:(UIImage *)image {
objc_setAssociatedObject(self, &kWRBackgroundImageKey, image, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
// set navigationBar backgroundImage
- (void)wr_setBackgroundImage:(UIImage *)image
{
[self.backgroundView removeFromSuperview];
self.backgroundView = nil;
if (self.backgroundImageView == nil)
{
// add a image(nil color) to _UIBarBackground make it clear
[self setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
if (self.subviews.count > 0)
{
self.backgroundImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.bounds), [WRNavigationBar navBarBottom])];
self.backgroundImageView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
// _UIBarBackground is first subView for navigationBar
[self.subviews.firstObject insertSubview:self.backgroundImageView atIndex:0];
}
}
self.backgroundImage = image;
self.backgroundImageView.image = image;
}
// set navigationBar barTintColor
- (void)wr_setBackgroundColor:(UIColor *)color
{
[self.backgroundImageView removeFromSuperview];
self.backgroundImageView = nil;
self.backgroundImage = nil;
if (self.backgroundView == nil)
{
// add a image(nil color) to _UIBarBackground make it clear
[self setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
self.backgroundView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.bounds), [WRNavigationBar navBarBottom])];
self.backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
// _UIBarBackground is first subView for navigationBar
[self.subviews.firstObject insertSubview:self.backgroundView atIndex:0];
}
self.backgroundView.backgroundColor = color;
}
// set _UIBarBackground alpha (_UIBarBackground subviews alpha <= _UIBarBackground alpha)
- (void)wr_setBackgroundAlpha:(CGFloat)alpha
{
UIView *barBackgroundView = self.subviews.firstObject;
if (@available(iOS 11.0, *))
{ // sometimes we can't change _UIBarBackground alpha
for (UIView *view in barBackgroundView.subviews) {
view.alpha = alpha;
}
} else {
barBackgroundView.alpha = alpha;
}
}
- (void)wr_setBarButtonItemsAlpha:(CGFloat)alpha hasSystemBackIndicator:(BOOL)hasSystemBackIndicator
{
for (UIView *view in self.subviews)
{
if (hasSystemBackIndicator == YES)
{
// _UIBarBackground/_UINavigationBarBackground对应的view是系统导航栏,不需要改变其透明度
Class _UIBarBackgroundClass = NSClassFromString(@"_UIBarBackground");
if (_UIBarBackgroundClass != nil)
{
if ([view isKindOfClass:_UIBarBackgroundClass] == NO) {
view.alpha = alpha;
}
}
Class _UINavigationBarBackground = NSClassFromString(@"_UINavigationBarBackground");
if (_UINavigationBarBackground != nil)
{
if ([view isKindOfClass:_UINavigationBarBackground] == NO) {
view.alpha = alpha;
}
}
}
else
{
// 这里如果不做判断的话,会显示 backIndicatorImage
if ([view isKindOfClass:NSClassFromString(@"_UINavigationBarBackIndicatorView")] == NO)
{
Class _UIBarBackgroundClass = NSClassFromString(@"_UIBarBackground");
if (_UIBarBackgroundClass != nil)
{
if ([view isKindOfClass:_UIBarBackgroundClass] == NO) {
view.alpha = alpha;
}
}
Class _UINavigationBarBackground = NSClassFromString(@"_UINavigationBarBackground");
if (_UINavigationBarBackground != nil)
{
if ([view isKindOfClass:_UINavigationBarBackground] == NO) {
view.alpha = alpha;
}
}
}
}
}
}
// 设置导航栏在垂直方向上平移多少距离 做移动导航栏时可用到
- (void)wr_setTranslationY:(CGFloat)translationY {
// CGAffineTransformMakeTranslation 平移
self.transform = CGAffineTransformMakeTranslation(0, translationY);
}
/** 获取当前导航栏在垂直方向上偏移了多少 */
- (CGFloat)wr_getTranslationY {
return self.transform.ty;
}
#pragma mark - call swizzling methods active 主动调用交换方法
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
SEL needSwizzleSelectors[1] = {
@selector(setTitleTextAttributes:)
};
for (int i = 0; i < 1; i++) {
SEL selector = needSwizzleSelectors[i];
NSString *newSelectorStr = [NSString stringWithFormat:@"wr_%@", NSStringFromSelector(selector)];
Method originMethod = class_getInstanceMethod(self, selector);
Method swizzledMethod = class_getInstanceMethod(self, NSSelectorFromString(newSelectorStr));
method_exchangeImplementations(originMethod, swizzledMethod);
}
});
}
- (void)wr_setTitleTextAttributes:(NSDictionary<NSString *,id> *)titleTextAttributes
{
NSMutableDictionary<NSString *,id> *newTitleTextAttributes = [titleTextAttributes mutableCopy];
if (newTitleTextAttributes == nil) {
return;
}
NSDictionary<NSString *,id> *originTitleTextAttributes = self.titleTextAttributes;
if (originTitleTextAttributes == nil) {
[self wr_setTitleTextAttributes:newTitleTextAttributes];
return;
}
__block UIColor *titleColor;
[originTitleTextAttributes enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
if ([key isEqual:NSForegroundColorAttributeName]) {
titleColor = (UIColor *)obj;
*stop = YES;
}
}];
if (titleColor == nil) {
[self wr_setTitleTextAttributes:newTitleTextAttributes];
return;
}
if (newTitleTextAttributes[NSForegroundColorAttributeName] == nil) {
newTitleTextAttributes[NSForegroundColorAttributeName] = titleColor;
}
[self wr_setTitleTextAttributes:newTitleTextAttributes];
}
@end
@interface UIViewController (Addition)
- (void)setPushToCurrentVCFinished:(BOOL)isFinished;
@end
//==========================================================================
#pragma mark - UINavigationController
//==========================================================================
@implementation UINavigationController (WRAddition)
static CGFloat wrPopDuration = 0.12;
static int wrPopDisplayCount = 0;
- (CGFloat)wrPopProgress
{
CGFloat all = 60 * wrPopDuration;
int current = MIN(all, wrPopDisplayCount);
return current / all;
}
static CGFloat wrPushDuration = 0.10;
static int wrPushDisplayCount = 0;
- (CGFloat)wrPushProgress
{
CGFloat all = 60 * wrPushDuration;
int current = MIN(all, wrPushDisplayCount);
return current / all;
}
- (UIStatusBarStyle)preferredStatusBarStyle {
return [self.topViewController wr_statusBarStyle];
}
- (void)setNeedsNavigationBarUpdateForBarBackgroundImage:(UIImage *)backgroundImage {
[self.navigationBar wr_setBackgroundImage:backgroundImage];
}
- (void)setNeedsNavigationBarUpdateForBarTintColor:(UIColor *)barTintColor {
[self.navigationBar wr_setBackgroundColor:barTintColor];
}
- (void)setNeedsNavigationBarUpdateForBarBackgroundAlpha:(CGFloat)barBackgroundAlpha {
[self.navigationBar wr_setBackgroundAlpha:barBackgroundAlpha];
}
- (void)setNeedsNavigationBarUpdateForTintColor:(UIColor *)tintColor {
self.navigationBar.tintColor = tintColor;
}
- (void)setNeedsNavigationBarUpdateForShadowImageHidden:(BOOL)hidden {
self.navigationBar.shadowImage = (hidden == YES) ? [UIImage new] : nil;
}
- (void)setNeedsNavigationBarUpdateForTitleColor:(UIColor *)titleColor
{
NSDictionary *titleTextAttributes = [self.navigationBar titleTextAttributes];
if (titleTextAttributes == nil) {
self.navigationBar.titleTextAttributes = @{NSForegroundColorAttributeName:titleColor};
return;
}
NSMutableDictionary *newTitleTextAttributes = [titleTextAttributes mutableCopy];
newTitleTextAttributes[NSForegroundColorAttributeName] = titleColor;
self.navigationBar.titleTextAttributes = newTitleTextAttributes;
}
- (void)updateNavigationBarWithFromVC:(UIViewController *)fromVC toVC:(UIViewController *)toVC progress:(CGFloat)progress
{
// change navBarBarTintColor
UIColor *fromBarTintColor = [fromVC wr_navBarBarTintColor];
UIColor *toBarTintColor = [toVC wr_navBarBarTintColor];
UIColor *newBarTintColor = [WRNavigationBar middleColor:fromBarTintColor toColor:toBarTintColor percent:progress];
[self setNeedsNavigationBarUpdateForBarTintColor:newBarTintColor];
// change navBarTintColor
UIColor *fromTintColor = [fromVC wr_navBarTintColor];
UIColor *toTintColor = [toVC wr_navBarTintColor];
UIColor *newTintColor = [WRNavigationBar middleColor:fromTintColor toColor:toTintColor percent:progress];
[self setNeedsNavigationBarUpdateForTintColor:newTintColor];
// change navBarTitleColor(在wr_popToViewController:animated:方法中直接改变标题颜色)
// UIColor *fromTitleColor = [fromVC wr_navBarTitleColor];
// UIColor *toTitleColor = [toVC wr_navBarTitleColor];
// UIColor *newTitleColor = [WRNavigationBar middleColor:fromTitleColor toColor:toTitleColor percent:progress];
// [self setNeedsNavigationBarUpdateForTitleColor:newTitleColor];
// change navBar _UIBarBackground alpha
CGFloat fromBarBackgroundAlpha = [fromVC wr_navBarBackgroundAlpha];
CGFloat toBarBackgroundAlpha = [toVC wr_navBarBackgroundAlpha];
CGFloat newBarBackgroundAlpha = [WRNavigationBar middleAlpha:fromBarBackgroundAlpha toAlpha:toBarBackgroundAlpha percent:progress];
[self setNeedsNavigationBarUpdateForBarBackgroundAlpha:newBarBackgroundAlpha];
}
#pragma mark - call swizzling methods active 主动调用交换方法
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
SEL needSwizzleSelectors[4] = {
NSSelectorFromString(@"_updateInteractiveTransition:"),
@selector(popToViewController:animated:),
@selector(popToRootViewControllerAnimated:),
@selector(pushViewController:animated:)
};
for (int i = 0; i < 4; i++) {
SEL selector = needSwizzleSelectors[i];
NSString *newSelectorStr = [[NSString stringWithFormat:@"wr_%@", NSStringFromSelector(selector)] stringByReplacingOccurrencesOfString:@"__" withString:@"_"];
Method originMethod = class_getInstanceMethod(self, selector);
Method swizzledMethod = class_getInstanceMethod(self, NSSelectorFromString(newSelectorStr));
method_exchangeImplementations(originMethod, swizzledMethod);
}
});
}
- (NSArray<UIViewController *> *)wr_popToViewController:(UIViewController *)viewController animated:(BOOL)animated
{ // pop 的时候导航栏标题颜色直接改变
[self setNeedsNavigationBarUpdateForTitleColor:[viewController wr_navBarTitleColor]];
__block CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(popNeedDisplay)];
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
[CATransaction setCompletionBlock:^{
[displayLink invalidate];
displayLink = nil;
wrPopDisplayCount = 0;
}];
[CATransaction setAnimationDuration:wrPopDuration];
[CATransaction begin];
NSArray<UIViewController *> *vcs = [self wr_popToViewController:viewController animated:animated];
[CATransaction commit];
return vcs;
}
- (NSArray<UIViewController *> *)wr_popToRootViewControllerAnimated:(BOOL)animated
{
__block CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(popNeedDisplay)];
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
[CATransaction setCompletionBlock:^{
[displayLink invalidate];
displayLink = nil;
wrPopDisplayCount = 0;
}];
[CATransaction setAnimationDuration:wrPopDuration];
[CATransaction begin];
NSArray<UIViewController *> *vcs = [self wr_popToRootViewControllerAnimated:animated];
[CATransaction commit];
return vcs;
}
// change navigationBar barTintColor smooth before pop to current VC finished
- (void)popNeedDisplay
{
if (self.topViewController != nil && self.topViewController.transitionCoordinator != nil)
{
wrPopDisplayCount += 1;
CGFloat popProgress = [self wrPopProgress];
UIViewController *fromVC = [self.topViewController.transitionCoordinator viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [self.topViewController.transitionCoordinator viewControllerForKey:UITransitionContextToViewControllerKey];
[self updateNavigationBarWithFromVC:fromVC toVC:toVC progress:popProgress];
}
}
- (void)wr_pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
__block CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(pushNeedDisplay)];
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
[CATransaction setCompletionBlock:^{
[displayLink invalidate];
displayLink = nil;
wrPushDisplayCount = 0;
[viewController setPushToCurrentVCFinished:YES];
}];
[CATransaction setAnimationDuration:wrPushDuration];
[CATransaction begin];
[self wr_pushViewController:viewController animated:animated];
[CATransaction commit];
}
// change navigationBar barTintColor smooth before push to current VC finished or before pop to current VC finished
- (void)pushNeedDisplay
{
if (self.topViewController != nil && self.topViewController.transitionCoordinator != nil)
{
wrPushDisplayCount += 1;
CGFloat pushProgress = [self wrPushProgress];
UIViewController *fromVC = [self.topViewController.transitionCoordinator viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [self.topViewController.transitionCoordinator viewControllerForKey:UITransitionContextToViewControllerKey];
[self updateNavigationBarWithFromVC:fromVC toVC:toVC progress:pushProgress];
}
}
#pragma mark - deal the gesture of return
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item
{
__weak typeof (self) weakSelf = self;
id<UIViewControllerTransitionCoordinator> coor = [self.topViewController transitionCoordinator];
if ([coor initiallyInteractive] == YES)
{
NSString *sysVersion = [[UIDevice currentDevice] systemVersion];
if ([sysVersion floatValue] >= 10)
{
[coor notifyWhenInteractionChangesUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
__strong typeof (self) pThis = weakSelf;
[pThis dealInteractionChanges:context];
}];
}
else
{
[coor notifyWhenInteractionEndsUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
__strong typeof (self) pThis = weakSelf;
[pThis dealInteractionChanges:context];
}];
}
return YES;
}
NSUInteger itemCount = self.navigationBar.items.count;
NSUInteger n = self.viewControllers.count >= itemCount ? 2 : 1;
UIViewController *popToVC = self.viewControllers[self.viewControllers.count - n];
[self popToViewController:popToVC animated:YES];
return YES;
}
// deal the gesture of return break off
- (void)dealInteractionChanges:(id<UIViewControllerTransitionCoordinatorContext>)context
{
void (^animations) (UITransitionContextViewControllerKey) = ^(UITransitionContextViewControllerKey key){
UIColor *curColor = [[context viewControllerForKey:key] wr_navBarBarTintColor];
CGFloat curAlpha = [[context viewControllerForKey:key] wr_navBarBackgroundAlpha];
[self setNeedsNavigationBarUpdateForBarTintColor:curColor];
[self setNeedsNavigationBarUpdateForBarBackgroundAlpha:curAlpha];
};
// after that, cancel the gesture of return
if ([context isCancelled] == YES)
{
double cancelDuration = [context transitionDuration] * [context percentComplete];
[UIView animateWithDuration:cancelDuration animations:^{
animations(UITransitionContextFromViewControllerKey);
}];
}
else
{
// after that, finish the gesture of return
double finishDuration = [context transitionDuration] * (1 - [context percentComplete]);
[UIView animateWithDuration:finishDuration animations:^{
animations(UITransitionContextToViewControllerKey);
}];
}
}
- (void)wr_updateInteractiveTransition:(CGFloat)percentComplete
{
UIViewController *fromVC = [self.topViewController.transitionCoordinator viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [self.topViewController.transitionCoordinator viewControllerForKey:UITransitionContextToViewControllerKey];
[self updateNavigationBarWithFromVC:fromVC toVC:toVC progress:percentComplete];
[self wr_updateInteractiveTransition:percentComplete];
}
@end
//==========================================================================
#pragma mark - UIViewController
//==========================================================================
@implementation UIViewController (WRAddition)
static char kWRPushToCurrentVCFinishedKey;
static char kWRPushToNextVCFinishedKey;
static char kWRNavBarBackgroundImageKey;
static char kWRNavBarBarTintColorKey;
static char kWRNavBarBackgroundAlphaKey;
static char kWRNavBarTintColorKey;
static char kWRNavBarTitleColorKey;
static char kWRStatusBarStyleKey;
static char kWRNavBarShadowImageHiddenKey;
static char kWRCustomNavBarKey;
// navigationBar barTintColor can not change by currentVC before fromVC push to currentVC finished
- (BOOL)pushToCurrentVCFinished
{
id isFinished = objc_getAssociatedObject(self, &kWRPushToCurrentVCFinishedKey);
return (isFinished != nil) ? [isFinished boolValue] : NO;
}
- (void)setPushToCurrentVCFinished:(BOOL)isFinished {
objc_setAssociatedObject(self, &kWRPushToCurrentVCFinishedKey, @(isFinished), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
// navigationBar barTintColor can not change by currentVC when currentVC push to nextVC finished
- (BOOL)pushToNextVCFinished
{
id isFinished = objc_getAssociatedObject(self, &kWRPushToNextVCFinishedKey);
return (isFinished != nil) ? [isFinished boolValue] : NO;
}
- (void)setPushToNextVCFinished:(BOOL)isFinished
{
objc_setAssociatedObject(self, &kWRPushToNextVCFinishedKey, @(isFinished), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
// navigationBar backgroundImage
- (UIImage *)wr_navBarBackgroundImage
{
UIImage *image = (UIImage *)objc_getAssociatedObject(self, &kWRNavBarBackgroundImageKey);
image = (image == nil) ? [WRNavigationBar defaultNavBarBackgroundImage] : image;
return image;
}
- (void)wr_setNavBarBackgroundImage:(UIImage *)image
{
if ([[self wr_customNavBar] isKindOfClass:[UINavigationBar class]])
{
// UINavigationBar *navBar = (UINavigationBar *)[self wr_customNavBar];
// [navBar wr_setBackgroundImage:image];
}
else {
objc_setAssociatedObject(self, &kWRNavBarBackgroundImageKey, image, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
}
// navigationBar barTintColor
- (UIColor *)wr_navBarBarTintColor
{
UIColor *barTintColor = (UIColor *)objc_getAssociatedObject(self, &kWRNavBarBarTintColorKey);
return (barTintColor != nil) ? barTintColor : [WRNavigationBar defaultNavBarBarTintColor];
}
- (void)wr_setNavBarBarTintColor:(UIColor *)color
{
objc_setAssociatedObject(self, &kWRNavBarBarTintColorKey, color, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if ([[self wr_customNavBar] isKindOfClass:[UINavigationBar class]])
{
// UINavigationBar *navBar = (UINavigationBar *)[self wr_customNavBar];
// [navBar wr_setBackgroundColor:color];
}
else
{
if (([self pushToCurrentVCFinished] == YES || [self isRootViewController] == YES) && [self pushToNextVCFinished] == NO) {
[self.navigationController setNeedsNavigationBarUpdateForBarTintColor:color];
}
}
}
// navigationBar _UIBarBackground alpha
- (CGFloat)wr_navBarBackgroundAlpha
{
id barBackgroundAlpha = objc_getAssociatedObject(self, &kWRNavBarBackgroundAlphaKey);
return (barBackgroundAlpha != nil) ? [barBackgroundAlpha floatValue] : [WRNavigationBar defaultNavBarBackgroundAlpha];
}
- (void)wr_setNavBarBackgroundAlpha:(CGFloat)alpha
{
objc_setAssociatedObject(self, &kWRNavBarBackgroundAlphaKey, @(alpha), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if ([[self wr_customNavBar] isKindOfClass:[UINavigationBar class]])
{
// UINavigationBar *navBar = (UINavigationBar *)[self wr_customNavBar];
// [navBar wr_setBackgroundAlpha:alpha];
}
else
{
if (([self pushToCurrentVCFinished] == YES || [self isRootViewController] == YES) && [self pushToNextVCFinished] == NO) {
[self.navigationController setNeedsNavigationBarUpdateForBarBackgroundAlpha:alpha];
}
}
}
// navigationBar tintColor
- (UIColor *)wr_navBarTintColor
{
UIColor *tintColor = (UIColor *)objc_getAssociatedObject(self, &kWRNavBarTintColorKey);
return (tintColor != nil) ? tintColor : [WRNavigationBar defaultNavBarTintColor];
}
- (void)wr_setNavBarTintColor:(UIColor *)color
{
objc_setAssociatedObject(self, &kWRNavBarTintColorKey, color, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if ([[self wr_customNavBar] isKindOfClass:[UINavigationBar class]])
{
// UINavigationBar *navBar = (UINavigationBar *)[self wr_customNavBar];
// navBar.tintColor = color;
}
else
{
if ([self pushToNextVCFinished] == NO) {
[self.navigationController setNeedsNavigationBarUpdateForTintColor:color];
}
}
}
// navigationBartitleColor
- (UIColor *)wr_navBarTitleColor
{
UIColor *titleColor = (UIColor *)objc_getAssociatedObject(self, &kWRNavBarTitleColorKey);
return (titleColor != nil) ? titleColor : [WRNavigationBar defaultNavBarTitleColor];
}
- (void)wr_setNavBarTitleColor:(UIColor *)color
{
objc_setAssociatedObject(self, &kWRNavBarTitleColorKey, color, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
if ([[self wr_customNavBar] isKindOfClass:[UINavigationBar class]])
{
// UINavigationBar *navBar = (UINavigationBar *)[self wr_customNavBar];
// navBar.titleTextAttributes = @{NSForegroundColorAttributeName:color};
}
else
{
if ([self pushToNextVCFinished] == NO) {
[self.navigationController setNeedsNavigationBarUpdateForTitleColor:color];
}
}
}
// statusBarStyle
- (UIStatusBarStyle)wr_statusBarStyle
{
id style = objc_getAssociatedObject(self, &kWRStatusBarStyleKey);
return (style != nil) ? [style integerValue] : [WRNavigationBar defaultStatusBarStyle];
}
- (void)wr_setStatusBarStyle:(UIStatusBarStyle)style
{
objc_setAssociatedObject(self, &kWRStatusBarStyleKey, @(style), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[self setNeedsStatusBarAppearanceUpdate];
}
// shadowImage
- (void)wr_setNavBarShadowImageHidden:(BOOL)hidden
{
objc_setAssociatedObject(self, &kWRNavBarShadowImageHiddenKey, @(hidden), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[self.navigationController setNeedsNavigationBarUpdateForShadowImageHidden:hidden];
}
- (BOOL)wr_navBarShadowImageHidden
{
id hidden = objc_getAssociatedObject(self, &kWRNavBarShadowImageHiddenKey);
return (hidden != nil) ? [hidden boolValue] : [WRNavigationBar defaultNavBarShadowImageHidden];
}
// custom navigationBar
- (UIView *)wr_customNavBar
{
UIView *navBar = objc_getAssociatedObject(self, &kWRCustomNavBarKey);
return (navBar != nil) ? navBar : [UIView new];
}
- (void)wr_setCustomNavBar:(UINavigationBar *)navBar
{
objc_setAssociatedObject(self, &kWRCustomNavBarKey, navBar, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
SEL needSwizzleSelectors[3] = {
@selector(viewWillAppear:),
@selector(viewWillDisappear:),
@selector(viewDidAppear:)
};
for (int i = 0; i < 3; i++) {
SEL selector = needSwizzleSelectors[i];
NSString *newSelectorStr = [NSString stringWithFormat:@"wr_%@", NSStringFromSelector(selector)];
Method originMethod = class_getInstanceMethod(self, selector);
Method swizzledMethod = class_getInstanceMethod(self, NSSelectorFromString(newSelectorStr));
method_exchangeImplementations(originMethod, swizzledMethod);
}
});
}
- (void)wr_viewWillAppear:(BOOL)animated
{
if ([self canUpdateNavigationBar] == YES)
{
[self setPushToNextVCFinished:NO];
[self.navigationController setNeedsNavigationBarUpdateForTintColor:[self wr_navBarTintColor]];
[self.navigationController setNeedsNavigationBarUpdateForTitleColor:[self wr_navBarTitleColor]];
}
[self wr_viewWillAppear:animated];
}
- (void)wr_viewWillDisappear:(BOOL)animated
{
if ([self canUpdateNavigationBar] == YES) {
[self setPushToNextVCFinished:YES];
}
[self wr_viewWillDisappear:animated];
}
- (void)wr_viewDidAppear:(BOOL)animated
{
if ([self isRootViewController] == NO) {
self.pushToCurrentVCFinished = YES;
}
if ([self canUpdateNavigationBar] == YES)
{
UIImage *barBgImage = [self wr_navBarBackgroundImage];
if (barBgImage != nil) {
[self.navigationController setNeedsNavigationBarUpdateForBarBackgroundImage:barBgImage];
} else {
[self.navigationController setNeedsNavigationBarUpdateForBarTintColor:[self wr_navBarBarTintColor]];
}
[self.navigationController setNeedsNavigationBarUpdateForBarBackgroundAlpha:[self wr_navBarBackgroundAlpha]];
[self.navigationController setNeedsNavigationBarUpdateForTintColor:[self wr_navBarTintColor]];
[self.navigationController setNeedsNavigationBarUpdateForTitleColor:[self wr_navBarTitleColor]];
[self.navigationController setNeedsNavigationBarUpdateForShadowImageHidden:[self wr_navBarShadowImageHidden]];
}
[self wr_viewDidAppear:animated];
}
- (BOOL)canUpdateNavigationBar
{
CGRect viewFrame = self.view.frame;
CGRect maxFrame = [UIScreen mainScreen].bounds;
CGRect middleFrame = CGRectMake(0, WRNavigationBar.navBarBottom, WRNavigationBar.screenWidth, WRNavigationBar.screenHeight-WRNavigationBar.navBarBottom);
CGRect minFrame = CGRectMake(0, WRNavigationBar.navBarBottom, WRNavigationBar.screenWidth, WRNavigationBar.screenHeight-WRNavigationBar.navBarBottom-WRNavigationBar.tabBarHeight);
// 蝙蝠