在iOS開發(fā)中,有時(shí)候我們需要自動隱藏或顯示UITabBar,本文將用一個(gè)完整的案例,演示如何用動畫的方式,來切換UITabBar的隱藏和顯示!—— 請看?
最終效果:

UITabBar自動隱藏和顯示
如果直接設(shè)置isHidden屬性,效果會顯得很生硬!——如果自己實(shí)現(xiàn)切換顯示和隱藏的效果,應(yīng)該怎么做呢?
實(shí)現(xiàn)思路:
- 在需要【顯示】的時(shí)候,將TabBar中心點(diǎn)的Y設(shè)置成:屏幕高度 - TabBar高度
- 在需要【隱藏】的時(shí)候,將TabBar中心點(diǎn)的Y設(shè)置成:屏幕高度
最簡單的做法,可以給UITabBar加上以下代碼:
func changeTabBar(hidden: Bool){
let ScreenHeight = UIScreen.main.bounds.height // 屏幕高度
let tabBarHeight: CGFloat = self.frame.height // TabBar自身的高度
UIView.animate(withDuration: 0.5) {
if hidden {
var frame = self.frame
frame.origin.y = ScreenHeight // TabBar中心點(diǎn)的Y:屏幕高度
self.frame = frame
} else {
var frame = self.frame
frame.origin.y = ScreenHeight - tabBarHeight // TabBar中心點(diǎn)的Y:屏幕高度 - TabBar高度
self.frame = frame
}
}
}
但是,以上這段代碼會有一個(gè)問題:
當(dāng)TabBar在隱藏的狀態(tài)下,屏幕切換橫屏或豎屏,TabBar會顯示出來!
因此,還需要做橫屏和豎屏的判斷,下面我們對代碼進(jìn)行一些改進(jìn) ……
具體的操作,我們直接看代碼(注釋已經(jīng)寫的很詳細(xì)了):
// 顯示|隱藏 TabBar 方法
func changeTabBar(hidden:Bool, animated: Bool){
if isAnimating { return } // 如果動畫正在執(zhí)行,則不執(zhí)行!
if self.isHidden == hidden { return } // 如果已經(jīng)是指定隱藏狀態(tài),則不執(zhí)行!
/*=========================================*/
// 修復(fù)在TabBar隱藏的情況下,旋轉(zhuǎn)屏幕動畫錯亂的Bug!
if self.isHidden {
self.frame = CGRect(
x: frame.minX,
y: UIScreen.main.bounds.height,
width: frame.width,
height: frame.height
) // 強(qiáng)行修改tabBar的Frame
}
/*=========================================*/
let frame = self.frame // 獲取自身的frame
let isOutScreen = Int(UIScreen.main.bounds.height - self.frame.minY) == 0 // 是否在屏幕外面,以此為判斷動畫方向的依據(jù)!
let a: CGFloat = isOutScreen ? -1 : 1 // 定義動畫方向 | 上下
let offset = a * frame.size.height // 設(shè)置動畫偏移量
let duration: TimeInterval = (animated ? 0.5 : 0.0) // 定義動畫持續(xù)時(shí)間
self.isHidden = false // 開始動畫之前,先開啟tabBar的顯示!
isAnimating = true // 標(biāo)記動畫【正在執(zhí)行】狀態(tài)
UIView.animate(
withDuration: duration,
animations: {
self.center.y += offset // 改變【中心點(diǎn)】偏移量!
}) { _ in
self.isHidden = !isOutScreen // 設(shè)置tabBar的顯示狀態(tài)
isAnimating = false // 取消動畫【正在執(zhí)行】狀態(tài)
}
}
如何使用:
首先,將以上方法,直接寫到UITabBar的擴(kuò)展里面,這樣方便復(fù)用!
然后,在需要控制TabBar隱藏和顯示的頁面,調(diào)用changeTabBar方法
一般情況下,我們是需要在詳情頁面隱藏TabBar,在返回主頁,顯示TabBar,因此,我們可以在detailVC頁面,重寫以下兩個(gè)方法:
// 頁面即將顯示的時(shí)候,隱藏TabBar?。?!
override func viewWillAppear(_ animated: Bool) {
parent?.tabBarController?.tabBar.changeTabBar(hidden: true, animated: true)
}
// 頁面即將消失的時(shí)候,顯示TabBar?。?!
override func viewWillDisappear(_ animated: Bool) {
parent?.tabBarController?.tabBar.changeTabBar(hidden: false, animated: true)
}
使用起來,還算簡單!
接下來,我們上完整案例代碼(似乎有點(diǎn)多,主要都是一些頁面布局的東西,但都很簡單!-)……
完整代碼:
- SceneDelegate.swift
//
// SceneDelegate.swift
// UIKit-basic
//
// Created by Qire_er on 2022/1/11.
//
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let myScene = (scene as? UIWindowScene) else { return }
let windon = UIWindow(windowScene: myScene)
windon.rootViewController = MainTabVC() // 設(shè)置根控制器,否則就白忙活了!
self.window = windon
windon.makeKeyAndVisible()
}
func sceneDidDisconnect(_ scene: UIScene) {}
func sceneDidBecomeActive(_ scene: UIScene) {}
func sceneWillResignActive(_ scene: UIScene) {}
func sceneWillEnterForeground(_ scene: UIScene) {}
func sceneDidEnterBackground(_ scene: UIScene) {}
}
- MainTabVC.swift【主TabBar控制器】
//
// MainTabVC.swift
// UIKit-basic
//
// Created by Qire_er on 2022/1/11.
//
import UIKit
class MainTabVC: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
let title = ["Chat", "Movie", "Read", "debug"]
// 創(chuàng)建 tab01_VC 控制器
let tab01_VC = TabVC()
tab01_VC.tabBarItem.image = UIImage(systemName: "bubble.left.and.bubble.right")
tab01_VC.tabBarItem.selectedImage = UIImage(systemName: "bubble.left.and.bubble.right.fill")
tab01_VC.tabBarItem.title = title[0]
// 創(chuàng)建 tab02_VC 控制器
let tab02_VC = TabVC()
tab02_VC.tabBarItem.image = UIImage(systemName: "play.rectangle")
tab02_VC.tabBarItem.selectedImage = UIImage(systemName: "play.rectangle.fill")
tab02_VC.tabBarItem.title = title[1]
// 創(chuàng)建 tab03_VC 控制器
let tab03_VC = TabVC()
tab03_VC.tabBarItem.image = UIImage(systemName: "book")
tab03_VC.tabBarItem.selectedImage = UIImage(systemName: "book.fill")
tab03_VC.tabBarItem.title = title[2]
// 創(chuàng)建 tab04_VC 控制器
let tab04_VC = TabVC()
tab04_VC.tabBarItem.image = UIImage(systemName: "ladybug")
tab04_VC.tabBarItem.selectedImage = UIImage(systemName: "ladybug.fill")
tab04_VC.tabBarItem.title = title[3]
let tab01_NC = UINavigationController(rootViewController: tab01_VC) // 創(chuàng)建 tab01_NC 導(dǎo)航控制器
let tab02_NC = UINavigationController(rootViewController: tab02_VC) // 創(chuàng)建 tab02_NC 導(dǎo)航控制器
let tab03_NC = UINavigationController(rootViewController: tab03_VC) // 創(chuàng)建 tab03_NC 導(dǎo)航控制器
let tab04_NC = UINavigationController(rootViewController: tab04_VC) // 創(chuàng)建 tab04_NC 導(dǎo)航控制器
tab01_NC.title = title[0]
tab02_NC.title = title[1]
tab03_NC.title = title[2]
tab04_NC.title = title[3]
self.tabBar.tintColor = .red // 選中顏色
self.tabBar.unselectedItemTintColor = .systemGray2 // 未選中顏色
self.viewControllers = [tab01_NC, tab02_NC, tab03_NC, tab04_NC]
}
}
private var isAnimating = false
extension UITabBar {
// 顯示|隱藏 TabBar 方法
func changeTabBar(hidden:Bool, animated: Bool){
if isAnimating { return } // 如果動畫正在執(zhí)行,則不執(zhí)行!
if self.isHidden == hidden { return } // 如果已經(jīng)是指定隱藏狀態(tài),則不執(zhí)行!
/*=========================================*/
// 修復(fù)在TabBar隱藏的情況下,旋轉(zhuǎn)屏幕動畫錯亂的Bug!
if self.isHidden {
self.frame = CGRect(
x: frame.minX,
y: UIScreen.main.bounds.height,
width: frame.width,
height: frame.height
) // 強(qiáng)行修改tabBar的Frame
}
/*=========================================*/
let frame = self.frame // 獲取自身的frame
let isOutScreen = Int(UIScreen.main.bounds.height - self.frame.minY) == 0 // 是否在屏幕外面,以此為判斷動畫方向的依據(jù)!
let a: CGFloat = isOutScreen ? -1 : 1 // 定義動畫方向 | 上下
let offset = a * frame.size.height // 設(shè)置動畫偏移量
let duration: TimeInterval = (animated ? 0.5 : 0.0) // 定義動畫持續(xù)時(shí)間
self.isHidden = false // 開始動畫之前,先開啟tabBar的顯示!
isAnimating = true // 標(biāo)記動畫【正在執(zhí)行】狀態(tài)
UIView.animate(
withDuration: duration,
animations: {
self.center.y += offset // 改變【中心點(diǎn)】偏移量!
}) { _ in
self.isHidden = !isOutScreen // 設(shè)置tabBar的顯示狀態(tài)
isAnimating = false // 取消動畫【正在執(zhí)行】狀態(tài)
}
}
}
- TabVC.swift【Tab子頁】
//
// TabVC.swift
// UIKit-basic
//
// Created by Qire_er on 2022/1/11.
//
import UIKit
class TabVC: UIViewController {
// 一些系統(tǒng)圖標(biāo)的名稱
let imgs = ["bubble.left.and.bubble.right.fill", "play.rectangle.fill", "book.fill", "ladybug.fill"]
override func viewDidLoad() {
super.viewDidLoad()
title = tabBarController?.selectedViewController?.title // 設(shè)置TabVC標(biāo)題
self.navigationController?.navigationBar.tintColor = .red // 設(shè)置返回按鈕顏色
let vStack = UIStackView()
vStack.translatesAutoresizingMaskIntoConstraints = false
vStack.axis = .vertical
vStack.distribution = .fillEqually
vStack.spacing = 8
let radius: CGFloat = 8
for (index, item) in imgs.enumerated() {
let isSelectedIndex = (index == tabBarController!.selectedIndex)
let btn = UIButton()
btn.setImage(UIImage(systemName: item), for: .normal)
btn.tintColor = .white
btn.backgroundColor = isSelectedIndex ? .red : .red.withAlphaComponent(0.05)
// 給按鈕添加一點(diǎn)點(diǎn)陰影效果(^-^)
if isSelectedIndex {
btn.setTitle(title, for: .normal)
btn.layer.shadowColor = UIColor.red.cgColor
btn.layer.shadowOpacity = 0.25
btn.layer.shadowOffset = CGSize(width: 5, height: 5)
btn.layer.shadowRadius = 10
}
btn.layer.cornerRadius = radius
vStack.addArrangedSubview(btn)
if isSelectedIndex {
btn.addTarget(self, action: #selector(showDetial), for: .touchUpInside)
}
}
view.addSubview(vStack)
NSLayoutConstraint.activate([
vStack.widthAnchor.constraint(equalToConstant: 100),
vStack.heightAnchor.constraint(equalToConstant: 400),
vStack.centerXAnchor.constraint(equalTo: view.centerXAnchor),
vStack.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
let btnWidth: CGFloat = 60
let tabBtn = UIButton()
tabBtn.setImage(UIImage(systemName: "arrow.up.arrow.down"), for: .normal)
tabBtn.translatesAutoresizingMaskIntoConstraints = false
tabBtn.tintColor = .white
tabBtn.backgroundColor = .blue.withAlphaComponent(0.75)
tabBtn.layer.cornerRadius = btnWidth * 0.5
tabBtn.addTarget(self, action: #selector(tabBarToggle), for: .touchUpInside)
// 也給它添加一點(diǎn)點(diǎn)陰影效果!
tabBtn.layer.shadowColor = UIColor.blue.cgColor
tabBtn.layer.shadowOpacity = 0.25
tabBtn.layer.shadowOffset = CGSize(width: 5, height: 5)
tabBtn.layer.shadowRadius = 10
view.addSubview(tabBtn)
// 添加約束!
NSLayoutConstraint.activate([
tabBtn.widthAnchor.constraint(equalToConstant: btnWidth),
tabBtn.heightAnchor.constraint(equalToConstant: btnWidth),
tabBtn.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -15),
tabBtn.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -120)
])
view.backgroundColor = .white
}
private var isTabBarHidden = false // TabBar是否隱藏
// 切換tabBar【顯示|隱藏】按鈕handler
@objc private func tabBarToggle() {
isTabBarHidden = !isTabBarHidden
parent?.tabBarController?.tabBar.changeTabBar(hidden: isTabBarHidden, animated: true)
}
// 顯示詳情頁方法
@objc private func showDetial() {
let detailVC = DetailView(image: UIImage(systemName: imgs[tabBarController!.selectedIndex])!) // 順便把圖片信息也傳過去~
navigationController?.pushViewController(detailVC, animated: true) // 將detailVC詳情頁push進(jìn)去!
}
}
- DetailView.swift【詳情頁】
//
// DetailView.swift
// UIKit-basic
//
// Created by Qire_er on 2022/1/11.
//
import UIKit
class DetailView: UIViewController {
init(image: UIImage) {
super.init(nibName: nil, bundle: nil)
title = "詳情" // 設(shè)置頁面標(biāo)題
let imgView = UIImageView(image: image)
imgView.translatesAutoresizingMaskIntoConstraints = false
imgView.contentMode = .scaleAspectFit
imgView.tintColor = .white
view.addSubview(imgView)
NSLayoutConstraint.activate([
imgView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 15),
imgView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -15),
imgView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
imgView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
view.backgroundColor = .red
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
// 頁面即將顯示的時(shí)候,隱藏TabBar?。。? override func viewWillAppear(_ animated: Bool) {
parent?.tabBarController?.tabBar.changeTabBar(hidden: true, animated: true)
}
// 頁面即將消失的時(shí)候,顯示TabBar?。。? override func viewWillDisappear(_ animated: Bool) {
parent?.tabBarController?.tabBar.changeTabBar(hidden: false, animated: true)
}
}
以上就是本案例的所有代碼,最后,送你一個(gè)好東西!(-) ……

NO! bug
(==完==)
ps: 以上僅代表個(gè)人淺見,如果你有什么高見,也歡迎討論交流!-