The easiest way to implement a custom tab bar
In iPadOS 18 and later, the default look and position of a tab bar has changed. It now floats over your content at the top.

Overall, itās a welcome change. Your app gets more space for content.
But what if you want your tab bar to stay consistent, same look and position, across all OS versions?
Thatās surprisingly hard to do.
Chances are youāve already customized the tab bar and now need to hide the new elevated one. Letās go over your options depending on the minimum deployment target.
Minimum deployment target: iOS 13
Thereās no official API to hide the default tab bar. Youāll need a workaround.
You can traverse the view hierarchy, find the UITabBarController
, and manually hide the tab bar:
struct HiddenTabBar: UIViewRepresentable {
init() {
UITabBar.appearance().isHidden = true
}
func makeUIView(context: Context) -> UIView {
let view = UIView()
view.backgroundColor = .clear
DispatchQueue.main.async {
if let tabController = view.tabBarController {
UITabBar.appearance().isHidden = false
if #available(iOS 18.0, *) {
tabController.isTabBarHidden = true
} else {
tabController.tabBar.isHidden = true
}
}
}
return view
}
}
// Usage
var body: some View {
TabView {
Text("Hello, world!")
.tabItem {
Label("1", systemImage: "1.circle")
}
.background {
HiddenTabBar() // The workaround to hide a tab bar
}
}
}
If Apple changes how the tab bar is implemented internally, your app may break.
Minimum deployment target: iOS 16
Apple introduced a new API to hide the default look of a tab bar:
ContentView()
.toolbar(.hidden, for: .navigationBar, .tabBar)
Unfortunately, thereās a bug. When switching tabs programmatically, the tab bar reappears.
Minimum deployment target: iOS 18
Apple introduced another new API, and this one works as expected:
ContentView()
.toolbarVisibility(.hidden, for: .navigationBar, .tabBar)
But letās be real, not many apps are shipping with iOS 18 as their minimum version yet.
So, if hiding the elevated tab bar isnāt reliable or available, how do we solve this?
By fully customizing the tab bar, the right way.
A custom tab bar
There are thousands of tutorials on how to do it, but most of them either rely on fragile workarounds or skip over key details.
Turns out, making a custom tab bar is actually simple. It only takes 4 steps:
- Inherit
UITabBarController
. - Hide the system tab bar.
- Adjust the safe area.
- Build your custom tab bar view.
// copy from real-life example
class TabBarController: UITabBarController { // 1
override func viewDidLoad() {
super.viewDidLoad()
// 1ļøā£
let tabBarHeight = 45.0
let bottomPadding = UIApplication.shared.windows
.first?.safeAreaInsets.bottom ?? 0
let customTabBarHeight = tabBarHeight + bottomPadding
// 2ļøā£
if #available(iOS 18.0, *) {
isTabBarHidden = true
else {
tabBar.isHidden = true
}
// Make the tab bar opaque
tabBar.isTranslucent = false
// 3ļøā£
additionalSafeAreaInsets = UIEdgeInsets(
top: 0, left: 0, bottom: tabBarHeight, right: 0
)
// 4ļøā£
let customTabBar: UIView = ...
view.addSubview(customTabBar)
customTabBar.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
customTabBar.leadingAnchor.constraint(equalTo: view.leadingAnchor),
customTabBar.trailingAnchor.constraint(equalTo: view.trailingAnchor),
customTabBar.heightAnchor.constraint(equalToConstant: customTabBarHeight),
customTabBar.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
}
}
Adjusting additionalSafeAreaInsets
ensures your content wonāt sit under your custom tab bar.
Happy building!