iOS Swift5從0到1系列(四):學習UINavigationController(2):底部TabBar的顯示與隱藏

UINavigationController 小系列(持續(xù)更新):

學習UINavigationController(1):基礎(chǔ)
學習UINavigationController(2):底部TabBar的顯示與隱藏

一、前言

上篇我們分享了 UINavigationController 基礎(chǔ)知識,以及導航欄左側(cè)按鈕的顯示規(guī)則,文章的最后,留了個小問題給讓大家思考,如何從一級頁面 push 到二級頁面時,隱藏 tabbar;當然,pop 時,再顯示 tabbar。

我們在源碼分析中,有講到過一個 Bool 變量,不知道大家有沒有特別注意過,或者自己去嘗試過,如下圖:

hidesBottomBarWhenPushed.png

上圖我特別標注了該變量,該變量是在 UIViewController 中的,\color{red}{(問題 1)}那如何使用呢?

本系列仿京東,我們看到大多數(shù)電商APP,一級頁面的導航欄都是自定義的,也有可能是隱藏;push 到二級頁面會使用系列默認的(顏色、左右按鈕個數(shù)可自定義),但肯定是顯示的;\color{red}{(問題 2)}如何控制導航欄的顯示與隱藏,以及如何自定義導航欄?

同時,我們還應該想到,不同的APP有不同的風格,而這風格可能是導航欄顏色不同,比如主流電商APP,導航欄可能都是紅色的,有些頁面則是白色;其它的APP可能因為不同的模塊功能而展示不同顏色,或者圖片;然而,這里會涉及到,用戶側(cè)滑返回是,導航欄的過渡過于生硬,如下圖:

navigation-bar-bg-color.gif

\color{red}{(問題 3)}如何優(yōu)化頁面切換時,導航欄的過渡顯的相對自然?

好了,先總結(jié)一下,我們遇到的問題:

  1. 當 UITabBarController 作為 rootViewController 時,嵌套 UINavigationController,在頁面切換時,如何做到 TabBar 顯示與隱藏自如?
  2. 有些頁面需要隱藏導航欄,有些又面要顯示,在頁面切換時,如何控制 NavigationBar 顯示與隱藏自如?
  3. 如何自定義不同風格的導航欄?
  4. 如何做到頁面切換時,導航欄的過渡自然而不生硬?

\color{red}{以上這些問題,我們將分篇章來依次學習如何處理,因為涉及的內(nèi)容會越來越多,一篇寫完,時間太久,也太累。}

二、掌控 TabBar 的顯示與隱藏

既然我們已經(jīng)說了,在 UIViewController 中有 hidesBottomBarWhenPushed = true 才能在 push 到下一個頁面時隱藏,那我們就先嘗試這個屬性,先嘗試在 HomeViewController 中嘗試一下,代碼如下:

class HomeViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        title = "首頁"
        
        // 若被 UITabBarController 嵌套,則設置該屬性進入下一個頁面時,隱藏底部 TabBar
        hidesBottomBarWhenPushed = true
        ......
    }
    ......
}

整體截圖如下:

home-vc.png

然后運行一下:

hidesBottomBarWhenPushed.gif

然后我們看到,push 到二級頁面,確實隱藏了 TabBar,但是 pop 時, TabBar 卻沒有再顯示...如果,這時也許有的人會說,我把這個屬性放到二級頁面中呢?我們可以試試:

  • 先去掉 HomeViewController 中添加的 hidesBottomBarWhenPushed 代碼;
  • 修改二級頁面,如下:
class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .red
        
        title = "二級頁面"
        
        // 若被 UITabBarController 嵌套,則設置該屬性進入下一個頁面時,隱藏底部 TabBar
        hidesBottomBarWhenPushed = true
    }
}

再運行,你會發(fā)現(xiàn),這回沒有任何的變化....為什么呢?那是因為,我們放置代碼的位置不對,我們要放在 init構(gòu)造器中才行!代碼如下:

class ViewController: UIViewController {
    
    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
        
        // 若被 UITabBarController 嵌套,則設置該屬性進入下一個頁面時,隱藏底部 TabBar
        hidesBottomBarWhenPushed = true
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .red
        
        title = "二級頁面"
    }
}

然后運行一下:

run-right.gif

OK! TabBar 在頁面切換時,我們確實解決了這個小問題,但是隨之又帶來了新問題:

  1. 我們 rootViewController 是 UITabBarController,有 5 個 UINavigationController + VC,難道每個對應的下一級頁面都要這么做?
  2. 實際項目中,我們所有的 UIViewController 肯定都有一個共同的基類:BaseViewController,一般不會有多個這樣的基類,但如果在 BaseViewController 的 init 構(gòu)造器中處理,其結(jié)果就會導致和開始的情況一樣,push 隱藏,pop 仍舊不會顯示,那該如何統(tǒng)一呢?

三、統(tǒng)一處理

3.1、統(tǒng)一基類 BaseViewController

首先,我們要統(tǒng)一基類,現(xiàn)在可能沒用,但常識上大家都知道,肯定會封裝一些內(nèi)容,這些封裝的內(nèi)容也會在后續(xù)文章中不斷的去豐富!

  1. 新建『New Group』,命名為:BaseControllers;
  2. 新建『New File...』,選擇『Cocoa Touch Class』,如下,然后點完成即可;
base-vc.png

該基類我們暫時什么都不干,然后,將我們之前創(chuàng)建的所有 ViewController 的繼承換成該 BaseViewController,例如:

re-inherit.png

3.2、創(chuàng)建繼承 UINavigationController 的子類 CustomNavigationController

CustomNavigationController.png

我們先來捋一捋思路:

  1. 在一級頁面中,添加 hidesBottomBarWhenPushed,雖然 push OK,但 pop 不OK;
  2. 在二級頁面中,只有在 init構(gòu)造器中添加 hidesBottomBarWhenPushed ,push & pop 才都OK;

從這兩條中,我們可以看出,hidesBottomBarWhenPushed 屬性需要添加在非嵌套在 UITabBarController 的 ViewController 上,且在初始化時添加才能正常,而我們 push 時,實際上是通過 UINavigationController 的 pushViewController 方法來進行跳轉(zhuǎn)的,因此,我們最好的解決方案就是:

  1. 繼承 UINavigationController;
  2. 重寫 pushViewController 方法;
  3. 根據(jù) UINavigationController 中的 viewControllers 該對象來判斷是否需要添加 hidesBottomBarWhenPushed 屬性;

具體做法如下:

class CustomNavigationController: UINavigationController {

    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    // 重寫 pushViewController 方法,不修改 pushViewController 的邏輯
    // 僅在跳轉(zhuǎn)前,判斷目標 VC 是否為一級頁面還是二級頁面,通過 viewControllers.count 來判斷:
    // viewControllers.count > 0,那么目標 VC 肯定是第二個頁面(即二級頁面);我們就添加上 hidesBottomBarWhenPushed = true
    override func pushViewController(_ viewController: UIViewController, animated: Bool) {
        if viewControllers.count > 0 {
            viewController.hidesBottomBarWhenPushed = true
        }
        super.pushViewController(viewController, animated: animated)
    }
}

如下圖:

cnc-realize.png

然后,修改 MainTabBarController,代碼如下:

class MainTabBarController: UITabBarController {
    ......
    func initTabBar() {
        let home = CustomNavigationController(rootViewController: HomeViewController())
        home.tabBarItem.title = "首頁"
        ......
    }
}

如下圖(5 個 VC 即一級頁面,都替換掉):

modify-main-tabbar-ctrl.png

恢復二級頁面的代碼:

class ViewController: BaseViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .red
        
        title = "二級頁面"
    }
}

最終,我們再次運行效果如下:

perfect-hides-and-show.gif

完美!至此,本篇內(nèi)容:正確的控制底部 TabBar 的顯示與隱藏就到這里,歡迎交流!謝謝。

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

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

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