Skip to content

Instantly share code, notes, and snippets.

@simme
Created January 25, 2018 15:36
Show Gist options
  • Star 45 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save simme/a44cd16f89038cbee8537b89d237386b to your computer and use it in GitHub Desktop.
Save simme/a44cd16f89038cbee8537b89d237386b to your computer and use it in GitHub Desktop.
Extension on UITabBarController for hiding/showing the tab bar.
extension UITabBarController {
/**
Show or hide the tab bar.
- Parameter hidden: `true` if the bar should be hidden.
- Parameter animated: `true` if the action should be animated.
- Parameter transitionCoordinator: An optional `UIViewControllerTransitionCoordinator` to perform the animation
along side with. For example during a push on a `UINavigationController`.
*/
func setTabBar(
hidden: Bool,
animated: Bool = true,
along transitionCoordinator: UIViewControllerTransitionCoordinator? = nil
) {
guard isTabBarHidden != hidden else { return }
let offsetY = hidden ? tabBar.frame.height : -tabBar.frame.height
let endFrame = tabBar.frame.offsetBy(dx: 0, dy: offsetY)
let vc: UIViewController? = viewControllers?[selectedIndex]
var newInsets: UIEdgeInsets? = vc?.additionalSafeAreaInsets
let originalInsets = newInsets
newInsets?.bottom -= offsetY
/// Helper method for updating child view controller's safe area insets.
func set(childViewController cvc: UIViewController?, additionalSafeArea: UIEdgeInsets) {
cvc?.additionalSafeAreaInsets = additionalSafeArea
cvc?.view.setNeedsLayout()
}
// Update safe area insets for the current view controller before the animation takes place when hiding the bar.
if hidden, let insets = newInsets { set(childViewController: vc, additionalSafeArea: insets) }
guard animated else {
tabBar.frame = endFrame
return
}
// Perform animation with coordinato if one is given. Update safe area insets _after_ the animation is complete,
// if we're showing the tab bar.
weak var tabBarRef = self.tabBar
if let tc = transitionCoordinator {
tc.animateAlongsideTransition(in: self.view, animation: { _ in tabBarRef?.frame = endFrame }) { context in
if !hidden, let insets = context.isCancelled ? originalInsets : newInsets {
set(childViewController: vc, additionalSafeArea: insets)
}
}
} else {
UIView.animate(withDuration: 0.3, animations: { tabBarRef?.frame = endFrame }) { didFinish in
if !hidden, didFinish, let insets = newInsets {
set(childViewController: vc, additionalSafeArea: insets)
}
}
}
}
/// `true` if the tab bar is currently hidden.
var isTabBarHidden: Bool {
return !tabBar.frame.intersects(view.frame)
}
}
@WhistlerW
Copy link

WhistlerW commented Sep 27, 2019

extension UITabBarController {

func setTabBar(
    hidden: Bool
) {
    guard isTabBarHidden != hidden else { return }
    
    let offsetY = hidden ? tabBar.frame.height : -tabBar.frame.height
    let endFrame = tabBar.frame.offsetBy(dx: 0, dy: offsetY)
    let cvc: UIViewController? = viewControllers?[selectedIndex]
    var newInsets: UIEdgeInsets?
    if #available(iOS 11.0, *) {
        newInsets = cvc?.additionalSafeAreaInsets
    }
    
    weak var tabBarRef = self.tabBar
    UIView.animate(withDuration: 0.1, animations: {
        
    }, completion: { isFinished in
        var height: CGFloat = 0
        if #available(iOS 11.0, *) {
            height = self.hasBottomNotch ? offsetY + (UIApplication.shared.keyWindow?.safeAreaInsets.bottom ?? 0.0) : offsetY
        } else {
            height = offsetY
        }
        newInsets?.bottom -= height
        if #available(iOS 11.0, *) {
            cvc?.additionalSafeAreaInsets = newInsets!
        } else {
            cvc?.view.frame = CGRect(x: 0, y: 0,
                                     width: cvc?.view.frame.width ?? 0.00,
                                     height: (cvc?.view.frame.height ?? 0.00) + height)
        }
        if isFinished {
            UIView.animate(withDuration: 0.2, animations: {
                tabBarRef?.frame = endFrame
            }, completion: nil)
        }
    })
}

var isTabBarHidden: Bool {
    return !tabBar.frame.intersects(view.frame)
}

}

Refactored

@simme
Copy link
Author

simme commented Sep 27, 2019

Sweet! I'm not using this myself anymore, but maybe others have use for it :)

@fukemy
Copy link

fukemy commented Mar 19, 2020

that only hide tabbar not tabbar item

@abishdangol
Copy link

I have recently used this extension. I found out one thing that after hiding the tabbar and then sending the app to background. Then again opening the app the tabbar reappears.

@BrentMifsud
Copy link

BrentMifsud commented Apr 9, 2021

I found this leaves a blank space if the view controller in your tab bar is a UIHostingViewController.

Heres the fix:

extension UITabBarController {
	/// Extends the size of the `UITabBarController` view frame, pushing the tab bar controller off screen.
	/// - Parameters:
	///   - hidden: Hide or Show the `UITabBar`
	///   - animated: Animate the change
	func setTabBarHidden(_ hidden: Bool, animated: Bool) {
		guard let vc = selectedViewController else { return }
		guard tabBarHidden != hidden else { return }
		
		let frame = self.tabBar.frame
		let height = frame.size.height
		let offsetY = hidden ? height : -height

		UIViewPropertyAnimator(duration: animated ? 0.3 : 0, curve: .easeOut) {
			self.tabBar.frame = self.tabBar.frame.offsetBy(dx: 0, dy: offsetY)
			self.selectedViewController?.view.frame = CGRect(
				x: 0,
				y: 0,
				width: vc.view.frame.width,
				height: vc.view.frame.height + offsetY
			)
			
			self.view.setNeedsDisplay()
			self.view.layoutIfNeeded()
		}
		.startAnimation()
	}
	
	/// Is the tab bar currently off the screen.
	private var tabBarHidden: Bool {
		tabBar.frame.origin.y >= UIScreen.main.bounds.height
	}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment