Is there a simple way to get a more customizable tab bar view using SwiftUI? I'm mainly asking from the perspective of macOS (though one that works on any system would be ideal), because the macOS implementation of the standard one has various issues:
- It has a rounded border around it, which means it looks awful with any sort of background color in the subviews.
- It doesn't support tab icons.
- It's very limited in terms of customization.
- It's buggy (sometimes it doesn't switch views as expected).
- It's pretty dated-looking.
Current code:
import SwiftUI
struct SimpleTabView: View {
@State private var selection = 0
var body: some View {
TabView(selection: $selection) {
HStack {
Spacer()
VStack {
Spacer()
Text("First Tab!")
Spacer()
}
Spacer()
}
.background(Color.blue)
.tabItem {
VStack {
Image("icons.general.home")
Text("Tab 1")
}
}
.tag(0)
HStack {
Spacer()
VStack {
Spacer()
Text("Second Tab!")
Spacer()
}
Spacer()
}
.background(Color.red)
.tabItem {
VStack {
Image("icons.general.list")
Text("Tab 2")
}
}
.tag(1)
HStack {
Spacer()
VStack {
Spacer()
Text("Third Tab!")
Spacer()
}
Spacer()
}
.background(Color.yellow)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.tabItem {
VStack {
Image("icons.general.cog")
Text("Tab 3")
}
}
.tag(2)
}
}
}


To address this, I've put together the following simple custom view which provides a more similar tab interface to iOS, even when running on Mac. It works just by taking an array of tuples, each one outlining the tab's title, icon name and content.
It works in both Light & Dark mode, and can be run on either macOS or iOS / iPadOS / etc., but you might want to just use the standard
TabViewimplementation when running on iOS; up to you.It also includes a parameter so you can position the bar at either the top or bottom, depending on preference (across the top fits better with macOS guidelines).
Here's an example of the result (in Dark Mode):
Here's the code. Some notes:
Colorso it can use system background colors, rather than hard-coding.background&shadowmodifiers, which are needed to prevent SwiftUI applying the shadow to every subview(!). Of course, if you don't want a shadow, you can just remove all of those lines (including thezIndex).Swift v5.1:
And here's an example of how you'd use it. Obviously, you could also pass it an entirely custom subview, rather than building them on the fly like this. Just make sure to wrap them inside that
AnyViewinitializer.The icons and their names are custom, so you'll have to use your own replacements.