How to hide tab bar with animation in iOS?

后端 未结 15 1921
夕颜
夕颜 2020-11-30 19:20

So I have a button that is connected to a IBAction. When I press the button I want to hide the tab bar in my iOS app with a animation. This [self setTabBarHidden:hidde

15条回答
  •  长情又很酷
    2020-11-30 20:21

    I went through the previous posts, so I came out with the solution below as subclass of UITabBarController

    Main points are:

    • Written in Swift 5.1
    • Xcode 11.3.1
    • Tested on iOS 13.3
    • Simulated on iPhone 11 and iPhone 8 (so with and without notch)
    • Handles the cases where the user taps on the different tabs
    • Handles the cases where we programmatically change the value of selectedIndex
    • Handles the view controller orientation changes
    • Handles the corner casere where the app moved to background and back to foreground

    Below the subclass TabBarController:

    class TabBarController: UITabBarController {
    
        //MARK: Properties
        
        private(set) var isTabVisible:Bool = true
        private var visibleTabBarFrame:CGRect = .zero
        private var hiddenTabBarFrame:CGRect = .zero
        
        override var selectedIndex: Int {
            didSet { self.updateTabBarFrames() }
        }
        
        //MARK: View lifecycle
        
        override func viewDidLoad() {
            super.viewDidLoad()
            self.delegate = self
            NotificationCenter.default.addObserver(self, selector: #selector(appWillEnterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil)
        }
        
        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
            self.calculateTabBarFrames()
        }
        
        override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
            super.viewWillTransition(to: size, with: coordinator)
            coordinator.animate(alongsideTransition: { (_) in }) { (_) in
                // when orientation changes, the tab bar frame changes, so we need to update it to the expected state
                self.calculateTabBarFrames()
                self.updateTabBarFrames()
            }
        }
        
        @objc private func appWillEnterForeground(_ notification:Notification){
            self.updateTabBarFrames()
        }
        
        //MARK: Private
        
        /// Calculates the frames of the tab bar and the expected bounds of the shown view controllers
        private func calculateTabBarFrames() {
            self.visibleTabBarFrame = self.tabBar.frame
            self.hiddenTabBarFrame = CGRect(x: self.visibleTabBarFrame.origin.x, y: self.visibleTabBarFrame.origin.y + self.visibleTabBarFrame.height, width: self.visibleTabBarFrame.width, height: self.visibleTabBarFrame.height)
        }
        
        /// Updates the tab bar and shown view controller frames based on the current expected tab bar visibility
        /// - Parameter tabIndex: if provided, it will update the view frame of the view controller for this tab bar index
        private func updateTabBarFrames(tabIndex:Int? = nil) {
            self.tabBar.frame = self.isTabVisible ? self.visibleTabBarFrame : self.hiddenTabBarFrame
            if let vc = self.viewControllers?[tabIndex ?? self.selectedIndex] {
                vc.additionalSafeAreaInsets.bottom = self.isTabVisible ? 0.0 : -(self.visibleTabBarFrame.height - self.view.safeAreaInsets.bottom)
    
            }
            self.view.layoutIfNeeded()
        }
        
        //MARK: Public
        
        /// Show/Hide the tab bar
        /// - Parameters:
        ///   - show: whether to show or hide the tab bar
        ///   - animated: whether the show/hide should be animated or not
        func showTabBar(_ show:Bool, animated:Bool = true) {
            guard show != self.isTabVisible else { return }
            self.isTabVisible = show
            guard animated else {
                self.tabBar.alpha = show ? 1.0 : 0.0
                self.updateTabBarFrames()
                return
            }
            UIView.animate(withDuration: 0.25, delay: 0.0, options: [.beginFromCurrentState,.curveEaseInOut], animations: {
                self.tabBar.alpha = show ? 1.0 : 0.0
                self.updateTabBarFrames()
            }) { (_) in }
        }
      
    }
    
    extension TabBarController: UITabBarControllerDelegate {
        override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
            if let tabIndex = self.tabBar.items?.firstIndex(of: item) {
                self.updateTabBarFrames(tabIndex: tabIndex)
            }
        }
    }
    

    Sample usage from within a shown view controller:

    // hide the tab bar animated (default)
    (self.tabBarController as? TabBarController)?.showTabBar(false)
    // hide the tab bar without animation
    (self.tabBarController as? TabBarController)?.showTabBar(false, animated:false)
    

    Sample output iPhone 11

    Sample output iPhone 8

    EDIT :

    • Updated the code to respect the safe area bottom inset
    • If you're experiencing issues with this solution and your tab bar contains a navigation controller as direct child in the viewControllers array, you may want to make sure that the navigation controller topViewController has the property extendedLayoutIncludesOpaqueBars set to true (you can set this directly from the Storyboard). This should resolve the problem

    Hope it helps someone :)

提交回复
热议问题