In SwiftUI, the TabView
APIs and customization is somewhat limited. We can often see tutorials and articles on how to create custom tab view components replicating similar layouts and behaviors. This time I want to show you very simple explorations I did last night with the native TabView
component that will add some sparks to your app.
The code is super simple, let's start with a basic TabView
layout in SwiftUI within our main view's body.
var body: some View {
TabView(selection: $selectedTab) {
FirstScreen()
.tabItem {
Image(systemName: "1.circle.fill")
Text("First tab")
}
.tag(RootTab.first)
SecondScreen()
.tabItem {
Image(systemName: "2.circle.fill")
Text("Second tab")
}
.tag(RootTab.second)
ThirdScreen()
.tabItem {
Image(systemName: "3.circle.fill")
Text("Third tab")
}
.tag(RootTab.third)
}
.background(Color.secondary)
.tint(.red)
}
A couple of things here:
- We are using the initializer of
TabView
that receives a Binding for the selected value. We will use it later also, but for now, we will declare a @State var in our host view:
@State private var selectedTab: RootTab = .second
- This is how it looks the RootTab enum, but you can basically use you own typed tabs.
internal enum RootTab: Int {
case first, second, third
/// We use this to color the light of each tab when selected.
var selectionColor: Color {
switch self {
case .first, .third:
return .purple
case .second:
return .main
}
}
}
Now, this is the standard look and feel of the TabView
, which doesn't bring that much joy :(
The idea is to add a light that focuses on the selected tab, and changes when the user switches to other tabs. For that, let's use an overlay modifier in out TabView
like follows:
TabView() {...}
.overlay(alignment: .bottom) {
let color = selectedTab.selectionColor
GeometryReader { geometry in
let aThird = geometry.size.width / 3
VStack {
Spacer()
Circle()
.background(color.blur(radius: 20))
.frame(width: aThird, height: 30)
.shadow(color: color, radius: 40)
.offset(
x: CGFloat(selectedTab.rawValue) * aThird,
y: 30
)
}
}
}
.edgesIgnoringSafeArea(.bottom)
How does it work?
- The overlay adds a
Circle
shape on top of ourTabView
. - We style the circle to have a shadow with big blur value that will simulate the light in the selected tab.
- The background modifier doesn't really need to be a blurred color but it enhanced the
light
effect a bit. You can play around and see how it works for you. - We set the width of the
Circle
to third of the screen's width by using aGeometryReader
to get the size of theTabView
. The height is up to you but it changes the strength of the shadow so again, play around to get the best results. - Then we use an offset modifier to position the
Circle
shape, this allow us to position the shape itself below theTabView
and letting only the shadow portion visible. - The horizontal offset(x axis), will be updated when the
selectedTab
changes, so the lightmoves
when the user switches tabs. - Last, but no least, the
edgesIgnoringSafeArea
allows the overlay to ignore the safe area and go beyond the screen.
Then, we can add a simple Spring animation when the selectedTab
value changes to give a better overall experience:
.animation(.spring(), value: selectedTab)
🎉 This is the final result:
It gets better on dark mode:
One thing to have in mind is that the overlay is on top of our TabView
, as the name says, so we have to be careful not to cover our tab items if we add something more than a light
.
That is it, simple and effective. I hope you enjoy the simplicity of SwiftUI like I do. 🚀
Top comments (0)