iOS 導(dǎo)航欄 - UINavigationBar

iOS開發(fā)中,UINavigationController是一種常用的視圖控制器,主要用來(lái)控制UI界面流程跳轉(zhuǎn)。
在UINavigationController使用過(guò)程中,經(jīng)常會(huì)遇到導(dǎo)航欄(UINavigationBar)顯示/隱藏、透明/不透明,以及透明漸變的問(wèn)題。

本文主要介紹導(dǎo)航欄(UINavigationBar)顯示/隱藏、透明/不透明,以及透明漸變的問(wèn)題。

以下是我整理的與導(dǎo)航欄相關(guān)的控件組織圖:


UINavigationController

通過(guò)上圖可以看出:
1、UINavigationController繼承于UIViewController;
2、UINavigationController包含viewControllers(UIViewController數(shù)組)、UINavigationBar、UIToolbar;
3、UINavigationBar管理items(UINavigationItem數(shù)組);
4、UIViewController包含UINavigationItem。

下圖能更好的理解UINavigationController組織結(jié)構(gòu)


UINavigationController

隱藏與顯示

方法一:
NS_CLASS_AVAILABLE_IOS(2_0) @interface UINavigationController : UIViewController

@property(nonatomic,getter=isNavigationBarHidden) BOOL navigationBarHidden;
- (void)setNavigationBarHidden:(BOOL)hidden animated:(BOOL)animated; 
@property(nonatomic,readonly) UINavigationBar *navigationBar;

@end

使用UINavigationController的navigationBarHidden屬性獲取隱藏導(dǎo)航方法。常用于以下方法中

- (void)viewWillAppear:(BOOL)animated; 
- (void)viewDidAppear:(BOOL)animated; 
- (void)viewWillDisappear:(BOOL)animated; 
- (void)viewDidDisappear:(BOOL)animated;

當(dāng)然還有實(shí)現(xiàn)UINavigationControllerDelegate,在代理方法中通過(guò)判斷showViewController類型,來(lái)控制顯示。

// Called when the navigation controller shows a new top view controller via a push, pop or setting of the view controller stack.
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated;
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated;

這種隱藏方式在滑動(dòng)過(guò)程中,有些iOS版本會(huì)出現(xiàn)過(guò)渡不自然現(xiàn)象。(現(xiàn)不建議使用)
效果如下:


導(dǎo)航欄隱藏與顯示.gif
方法二:

使用UINavigationController+FDFullscreenPopGesture
該類重寫了UINavigationController的+ (void)load;方法。
具體可參照以下代碼:

+ (void)load {
    // Inject "-pushViewController:animated:"
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];
        
        SEL originalSelector = @selector(pushViewController:animated:);
        SEL swizzledSelector = @selector(fd_pushViewController:animated:);
        
        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
        
        BOOL success = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
        if (success) {
            class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

運(yùn)用了runtime技術(shù),在執(zhí)行-(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated時(shí),執(zhí)行- (void)setNavigationBarHidden:(BOOL)hidden animated:(BOOL)animated;,以此來(lái)解決滑動(dòng)過(guò)程中,過(guò)渡不自然問(wèn)題。

該方法有一個(gè)不算缺陷的缺陷,就是一個(gè)工程只能只能置換一次。
如果你在一個(gè)SDK中用到了該技術(shù)(修改類別名),并且另一個(gè)工程引入了該SDK,并且也使用了該技術(shù)。會(huì)造成滑動(dòng)不自然。當(dāng)然,你可以把該技術(shù)單獨(dú)提出來(lái),供SDK和工程共同引用,這樣就可以解決問(wèn)題了。

透明與不透明,以及透明漸變

方法一:
NS_CLASS_AVAILABLE_IOS(2_0) @interface UINavigationBar : UIView <NSCoding, UIBarPositioning> 

/*
 New behavior on iOS 7.
 Default is YES.
 You may force an opaque background by setting the property to NO.
 If the navigation bar has a custom background image, the default is inferred 
 from the alpha values of the image—YES if it has any pixel with alpha < 1.0
 If you send setTranslucent:YES to a bar with an opaque custom background image
 it will apply a system opacity less than 1.0 to the image.
 If you send setTranslucent:NO to a bar with a translucent custom background image
 it will provide an opaque background for the image using the bar's barTintColor if defined, or black
 for UIBarStyleBlack or white for UIBarStyleDefault if barTintColor is nil.
 */
// Default is NO on iOS 6 and earlier. Always YES if barStyle is set to UIBarStyleBlackTranslucent
@property(nonatomic,assign,getter=isTranslucent) BOOL translucent NS_AVAILABLE_IOS(3_0) UI_APPEARANCE_SELECTOR; 

@end
方法二:

通過(guò)文章剛開始介紹的UINavigationController組織圖,可發(fā)現(xiàn)UINavigationController中只包含一個(gè)UINavigationBar。那么我們可以從UINavigationBar直接入手。

self.edgesForExtendedLayout = UIRectEdgeTop;
[self.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
if ([self.navigationController.navigationBar respondsToSelector:@selector(shadowImage)]) {
   [self.navigationController.navigationBar setShadowImage:[UIImage new]];
}
self.navigationController.navigationBar.backgroundColor = [UIColor clearColor];
self.navigationController.navigationBar.alpha = 0.0;
self.navigationController.navigationBar.backItem.hidesBackButton = YES;
self.navigationController.navigationItem.hidesBackButton = YES;
self.navigationItem.hidesBackButton = YES;

該方法是直接控制UINavigationBar的背景圖片、陰影圖片、背景色、navigationItem等,也能達(dá)到類似- (void)setNavigationBarHidden:(BOOL)hidden animated:(BOOL)animated;效果。
常用于以下方法中

- (void)viewWillAppear:(BOOL)animated; 
- (void)viewDidAppear:(BOOL)animated; 
- (void)viewWillDisappear:(BOOL)animated; 
- (void)viewDidDisappear:(BOOL)animated;

當(dāng)然還有實(shí)現(xiàn)UINavigationControllerDelegate,在代理方法中通過(guò)判斷showViewController類型,來(lái)控制顯示。

// Called when the navigation controller shows a new top view controller via a push, pop or setting of the view controller stack.
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated;
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated;

這種方法好處是直接控制UINavigationBar,不好的地方是調(diào)用的方法過(guò)多,還要考慮viewControllers中的navigationItem。

透明漸變

效果圖


透明漸變

主要實(shí)現(xiàn)思路代碼

#pragma mark - UIScrollViewDelegate

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    CGFloat offset = scrollView.contentOffset.y;
    [self setLifeNav:offset];
}
#pragma mark - Nav Bar

- (CGFloat)navBarColorAlpha:(CGFloat)offsetY {
    CGFloat alpha;
    CGFloat height = 200.0;
    if (offsetY <= 0) {
        alpha = 0.0;
    } else if (offsetY > 0 && offsetY < height) {
        alpha = offsetY / height;
    } else {
        alpha = 1.0;
    }
    return alpha;
}

/// 使用顏色填充圖片
- (UIImage *)imageWithColor:(UIColor *)color
{
    // 描述矩形
    CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
    // 開啟位圖上下文
    UIGraphicsBeginImageContext(rect.size);
    // 獲取位圖上下文
    CGContextRef context = UIGraphicsGetCurrentContext();
    // 使用color演示填充上下文
    CGContextSetFillColorWithColor(context, [color CGColor]);
    // 渲染上下文
    CGContextFillRect(context, rect);
    // 從上下文中獲取圖片
    UIImage *theImage = UIGraphicsGetImageFromCurrentImageContext();
    // 結(jié)束上下文
    UIGraphicsEndImageContext();
    return theImage;
}


- (void)setLifeNav:(CGFloat)offset {
    CGFloat maxHeight;
    if (@available(iOS 11.0, *)) {
        UIEdgeInsets insets = [[UIApplication sharedApplication] keyWindow].safeAreaInsets;
        maxHeight = 200.0 - MAX(insets.top, 20.0) - 44.0;
    }else {
        maxHeight = 200.0 - 64.0;
    }
    if (offset < maxHeight) {
        if ([self.window.rootViewController isKindOfClass:[UITabBarController class]]) {
            UITabBarController *tabC = (UITabBarController *)self.window.rootViewController;
            for (UINavigationController *navC in tabC.viewControllers) {
                if ([navC.topViewController isKindOfClass:[LifeViewController class]]) {
                    CGFloat alpha = [self navBarColorAlpha:offset];
                    UIImage *image = [self imageWithColor:[UIColor colorWithRed:60/255.0 green:131/255.0 blue:255/255.0 alpha:alpha]];
                    [navC.navigationBar setBackgroundImage:image forBarMetrics:UIBarMetricsDefault];
                    break;
                }
            }
        }
    }else {
        if ([self.window.rootViewController isKindOfClass:[UITabBarController class]]) {
            UITabBarController *tabC = (UITabBarController *)self.window.rootViewController;
            for (UINavigationController *navC in tabC.viewControllers) {
                if ([navC.topViewController isKindOfClass:[LifeViewController class]]) {
                    UIImage *image = [self imageWithColor:[UIColor colorWithRed:60/255.0 green:131/255.0 blue:255/255.0 alpha:1.0]];
                    [navC.navigationBar setBackgroundImage:image forBarMetrics:UIBarMetricsDefault];
                    break;
                }
            }
        }
    }
}

結(jié)束語(yǔ)

當(dāng)UINavigationBar完全透明時(shí),也可達(dá)到隱藏導(dǎo)航欄效果。

結(jié)尾附上Demo地址(GitHub)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容