iOS開發 導航欄漸變 圖片下拉放大 導航欄移動 仿QQ 微博 知乎導航欄



開發中,我們時常會遇到導航欄顏色漸變,導航欄移動等功能;只需要複製以下系統和自定義導航欄代碼到你新建的文件中,然後仿照試圖控制器所舉的例子使用即可,簡單易用,別忘記在單例裏面實現以下方法用以設置導航欄默認屬性

//設置默認屬性

- (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 <UIKit/UIKit.h>

@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 <objc/runtime.h>


@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);

    // 蝙蝠

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章