如何優(yōu)雅的處理導(dǎo)航欄的顯示與隱藏

問題所在

現(xiàn)在很多App為了追求美觀、簡(jiǎn)潔,其中的一些頁(yè)面都設(shè)計(jì)為導(dǎo)航欄隱藏的效果,在有導(dǎo)航欄和沒導(dǎo)航欄的頁(yè)面來回切換,就要在隱藏導(dǎo)航欄的頁(yè)面做好導(dǎo)航欄的顯示、隱藏的處理,而且還不知道跳轉(zhuǎn)到下一個(gè)頁(yè)面或返回上一個(gè)頁(yè)面的時(shí)候到底會(huì)存在什么問題,下面講一講我們公司的處理方式(借鑒了一些想法,也增加了一些自己優(yōu)化的點(diǎn))。

看下Demo效果


  錄屏效果有限,歡迎下載Demo體驗(yàn):https://github.com/ZhaoheMHz/CommonNavigationController

思路及具體實(shí)現(xiàn)

導(dǎo)航欄的處理,如果分散開到各個(gè)UIViewController中,則之后的管理和維護(hù)會(huì)變得比較麻煩,這里統(tǒng)一在UINavigationController中處理,所以我們創(chuàng)建一個(gè)UINavigationController的子類



  我們要做的還有就是在導(dǎo)航欄在push或pop的時(shí)候,對(duì)即將出現(xiàn)的頁(yè)面進(jìn)行導(dǎo)航欄的隱藏或顯示,這樣處理有兩個(gè)方法
  1、將當(dāng)前展示的頁(yè)面設(shè)置為self.navigation.delegate,在代理方法

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated;

中,對(duì)導(dǎo)航欄進(jìn)行隱藏或者顯示的操作
  2、在即將出現(xiàn)的頁(yè)面的viewWillAppear和viewWillDisappear中處理導(dǎo)航欄
  方法二經(jīng)實(shí)踐是不方便、不高效也容易出現(xiàn)問題的。而第一種方法需要在各個(gè)頁(yè)面設(shè)置代理,也是比較麻煩的,所以我們統(tǒng)一在ZHNavigationController中進(jìn)行代理方法的操作,我們把ZHNavigationController的delegate設(shè)置為自身,所有代理方法的處理都在ZHNavigationController中處理。
  ZHNavigationViewController.m中:

- (instancetype)init {
    if (self == [super init]) {
        self.delegate = self;
    }
    
    return self;
}

- (instancetype)initWithRootViewController:(UIViewController *)rootViewController {
    if (self == [super initWithRootViewController:rootViewController]) {
        self.delegate = self;
    }
    
    return self;
}

- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    // 判斷頁(yè)面導(dǎo)航欄是否需要隱藏
    BOOL isNavHidden = NO;
    if ([viewController isKindOfClass:[NavHideViewController class]]) {
        isNavHidden = YES;
    }
    
    [self setNavigationBarHidden:isNavHidden animated:YES];
}

前兩個(gè)init方法,用于設(shè)置代理,然后最后的代理方法中處理我們的導(dǎo)航欄的隱藏和顯示,在willShowViewController方法的if中,是我們需要隱藏導(dǎo)航欄的UIViewController,如果還想隱藏其他頁(yè)面的導(dǎo)航欄,直接新增一個(gè)||判斷條件就好了:

  if ([viewController isKindOfClass:[NavHideViewController class]] || [viewController isKindOfClass:[另一個(gè)Controller class]]) {
        isNavHidden = YES;
    }

然后這里有一點(diǎn)需要注意的是,就是,如果一個(gè)UIViewController的導(dǎo)航欄隱藏了,則這個(gè)頁(yè)面的滑動(dòng)返回手勢(shì)也失效了,所以我們要將這個(gè)手勢(shì)加回來,同樣是在ZHNavigationController中:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self slideBack];
}

#pragma mark - 處理滑動(dòng)返回
- (void)slideBack{
    // 設(shè)置自身的滑動(dòng)返回手勢(shì)的代理為自身
    self.interactivePopGestureRecognizer.delegate = self;
}

// 代理方法,在滑動(dòng)手勢(shì)即將開始的時(shí)候調(diào)用,返回YES則允許滑動(dòng)返回,返回NO則滑動(dòng)手勢(shì)無效
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
    // 這里只在當(dāng)前UINavigationController的棧中有兩個(gè)以上的Controller的時(shí)候可以滑動(dòng)返回,也就是如果當(dāng)前顯示的是RootViewController,則不允許滑動(dòng)返回,加上這個(gè)處理,可以防止出現(xiàn)在RootViewController也在屏幕左側(cè)滑動(dòng)導(dǎo)致頁(yè)面假死的情況出現(xiàn)
    if (self.viewControllers.count >= 2) {
        return YES;
    }
    
    return NO;
}

一點(diǎn)建議

如果有些頁(yè)面需要做到導(dǎo)航欄透明,但上面的UIBarButtonItem還在的效果,或者有些頁(yè)面要做導(dǎo)航欄隨著頁(yè)面的滾動(dòng)而出現(xiàn)漸變的效果,這種情況建議還是做一個(gè)假的導(dǎo)航欄放在上面,否則對(duì)一個(gè)頁(yè)面的導(dǎo)航欄的透明度做處理,與隱藏、顯示一樣,是非常容易在頁(yè)面切換的時(shí)候出現(xiàn)問題。
  下面提供一種比較方便的假導(dǎo)航欄的處理方式,這種方式可以繼續(xù)按照導(dǎo)航欄的leftBarButtonItem、rightBarButtonItem、middleView的方式進(jìn)行導(dǎo)航欄的配置,而不需要自己重新寫布局。

假導(dǎo)航

思路主要是,再創(chuàng)建一個(gè)UINavigationController,然后給他指定一個(gè)新的RootViewController,將新的UINavigationController的navigationBar添加到當(dāng)前頁(yè)面的View上,當(dāng)前頁(yè)面將這兩個(gè)Controller都設(shè)置為屬性,所有導(dǎo)航的相關(guān)的操作,都在這兩個(gè)屬性上進(jìn)行操作。

@interface NavFakeViewController ()

@property (nonatomic, strong) UINavigationController *fakeNav;
@property (nonatomic, strong) UIViewController *fakeSelf;

@end

@implementation NavFakeViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    /** 創(chuàng)建假的導(dǎo)航、rootViewControler */
    self.fakeSelf = [[UIViewController alloc] init];
    self.fakeNav = [[UINavigationController alloc] initWithRootViewController:self.fakeSelf];
    
    /** 將假的導(dǎo)航欄放到當(dāng)前頁(yè)面上 */
    self.fakeNav.navigationBar.frame = CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 64);
    [self.view addSubview:self.fakeNav.navigationBar];
    
    
    
    /** 配置一些按鈕 */
    UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 44, 44)];
    button.backgroundColor = [UIColor redColor];
    // 這里設(shè)置導(dǎo)航按鈕時(shí),要設(shè)置給self.fakeSelf
    self.fakeSelf.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:button];
    
    // 而要拿到navigationBar去做一些透明處理,一定要用下面的方式取出
//    self.fakeNav.navigationBar
}

@end
最后編輯于
?著作權(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)容