【iOS】UITabBar自动隐藏和显示(swift)

在iOS开发中,有时候我们需要自动隐藏或显示UITabBar,本文将用一个完整的案例,演示如何用动画的方式,来切换UITabBar的隐藏和显示!—— 请看️

最终效果:

UITabBar自动隐藏和显示

如果直接设置isHidden属性,效果会显得很生硬!——如果自己实现切换显示和隐藏的效果,应该怎么做呢?

实现思路:

  1. 在需要【显示】的时候,将TabBar中心点的Y设置成:屏幕高度 - TabBar高度
  2. 在需要【隐藏】的时候,将TabBar中心点的Y设置成:屏幕高度

最简单的做法,可以给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中心点的Y:屏幕高度
            self.frame = frame
        } else {
            var frame = self.frame
            frame.origin.y = ScreenHeight - tabBarHeight // TabBar中心点的Y:屏幕高度 - TabBar高度
            self.frame = frame
        }
    }
}

但是,以上这段代码会有一个问题:

当TabBar在隐藏的状态下,屏幕切换横屏或竖屏,TabBar会显示出来!

因此,还需要做横屏和竖屏的判断,下面我们对代码进行一些改进 ……
具体的操作,我们直接看代码(注释已经写的很详细了):

// 显示|隐藏 TabBar 方法
func changeTabBar(hidden:Bool, animated: Bool){
    if isAnimating { return }   // 如果动画正在执行,则不执行!
    if self.isHidden == hidden { return }  // 如果已经是指定隐藏状态,则不执行!
    
    /*=========================================*/
    // 修复在TabBar隐藏的情况下,旋转屏幕动画错乱的Bug!
    if self.isHidden {
        self.frame = CGRect(
            x: frame.minX,
            y: UIScreen.main.bounds.height,
            width: frame.width,
            height: frame.height
        ) // 强行修改tabBar的Frame
    }
    /*=========================================*/
    
    let frame = self.frame  // 获取自身的frame
    let isOutScreen = Int(UIScreen.main.bounds.height - self.frame.minY) == 0   // 是否在屏幕外面,以此为判断动画方向的依据!
    let a: CGFloat = isOutScreen ? -1 : 1   // 定义动画方向 | 上下
    let offset = a * frame.size.height  // 设置动画偏移量
    let duration: TimeInterval = (animated ? 0.5 : 0.0) // 定义动画持续时间
    
    self.isHidden = false // 开始动画之前,先开启tabBar的显示!
    isAnimating = true // 标记动画【正在执行】状态
    
    UIView.animate(
        withDuration: duration,
        animations: {
            self.center.y += offset // 改变【中心点】偏移量!
        }) { _ in
        self.isHidden = !isOutScreen    // 设置tabBar的显示状态
        isAnimating = false // 取消动画【正在执行】状态
    }
}

如何使用:

首先,将以上方法,直接写到UITabBar的扩展里面,这样方便复用!
然后,在需要控制TabBar隐藏和显示的页面,调用changeTabBar方法

一般情况下,我们是需要在详情页面隐藏TabBar,在返回主页,显示TabBar,因此,我们可以在detailVC页面,重写以下两个方法:

// 页面即将显示的时候,隐藏TabBar!!!
override func viewWillAppear(_ animated: Bool) {
    parent?.tabBarController?.tabBar.changeTabBar(hidden: true, animated: true)
}

// 页面即将消失的时候,显示TabBar!!!
override func viewWillDisappear(_ animated: Bool) {
    parent?.tabBarController?.tabBar.changeTabBar(hidden: false, animated: true)
}

使用起来,还算简单!

接下来,我们上完整案例代码(似乎有点多,主要都是一些页面布局的东西,但都很简单!-)……

完整代码:

  1. 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() // 设置根控制器,否则就白忙活了!
        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) {}
}
  1. 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"]
        
        // 创建 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]
        
        // 创建 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]
        
        // 创建 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]
        
        // 创建 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)   // 创建 tab01_NC 导航控制器
        let tab02_NC = UINavigationController(rootViewController: tab02_VC)   // 创建 tab02_NC 导航控制器
        let tab03_NC = UINavigationController(rootViewController: tab03_VC)   // 创建 tab03_NC 导航控制器
        let tab04_NC = UINavigationController(rootViewController: tab04_VC)   // 创建 tab04_NC 导航控制器
        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 }   // 如果动画正在执行,则不执行!
        if self.isHidden == hidden { return }  // 如果已经是指定隐藏状态,则不执行!
        
        /*=========================================*/
        // 修复在TabBar隐藏的情况下,旋转屏幕动画错乱的Bug!
        if self.isHidden {
            self.frame = CGRect(
                x: frame.minX,
                y: UIScreen.main.bounds.height,
                width: frame.width,
                height: frame.height
            ) // 强行修改tabBar的Frame
        }
        /*=========================================*/
        
        let frame = self.frame  // 获取自身的frame
        let isOutScreen = Int(UIScreen.main.bounds.height - self.frame.minY) == 0   // 是否在屏幕外面,以此为判断动画方向的依据!
        let a: CGFloat = isOutScreen ? -1 : 1   // 定义动画方向 | 上下
        let offset = a * frame.size.height  // 设置动画偏移量
        let duration: TimeInterval = (animated ? 0.5 : 0.0) // 定义动画持续时间
        
        self.isHidden = false // 开始动画之前,先开启tabBar的显示!
        isAnimating = true // 标记动画【正在执行】状态
        
        UIView.animate(
            withDuration: duration,
            animations: {
                self.center.y += offset // 改变【中心点】偏移量!
            }) { _ in
            self.isHidden = !isOutScreen    // 设置tabBar的显示状态
            isAnimating = false // 取消动画【正在执行】状态
        }
    }
}
  1. TabVC.swift【Tab子页】
//
//  TabVC.swift
//  UIKit-basic
//
//  Created by Qire_er on 2022/1/11.
//

import UIKit

class TabVC: UIViewController {
    
    // 一些系统图标的名称
    let imgs = ["bubble.left.and.bubble.right.fill", "play.rectangle.fill", "book.fill", "ladybug.fill"]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        title = tabBarController?.selectedViewController?.title // 设置TabVC标题
        self.navigationController?.navigationBar.tintColor = .red   // 设置返回按钮颜色
        
        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)
            
            // 给按钮添加一点点阴影效果(^-^)
            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)
        
        // 也给它添加一点点阴影效果!
        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进去!
    }
}
  1. 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 = "详情" // 设置页面标题
        
        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)
    }
    
    // 页面即将显示的时候,隐藏TabBar!!!
    override func viewWillAppear(_ animated: Bool) {
        parent?.tabBarController?.tabBar.changeTabBar(hidden: true, animated: true)
    }
    
    // 页面即将消失的时候,显示TabBar!!!
    override func viewWillDisappear(_ animated: Bool) {
        parent?.tabBarController?.tabBar.changeTabBar(hidden: false, animated: true)
    }
}

以上就是本案例的所有代码,最后,送你一个好东西!(-) ……

NO! bug

(==完==)

ps: 以上仅代表个人浅见,如果你有什么高见,也欢迎讨论交流!-

你可能感兴趣的:(【iOS】UITabBar自动隐藏和显示(swift))