Light up your SwiftUI TabView
Tags: swiftui, swift, ios, iosdev
Published: 2023-03-27 10:53:07

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?
1. The overlay adds a `Circle` shape on top of our `TabView`.
2. We style the circle to have a shadow with big blur value that will simulate the light in the selected tab. 
3. 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.
4. We set the width of the `Circle` to third of the screen's width by using a `GeometryReader` to get the size of the `TabView`. The height is up to you but it changes the strength of the shadow so again, play around to get the best results.
5. Then we use an offset modifier to position the `Circle` shape, this allow us to position the shape itself below the `TabView` and letting only the shadow portion visible.
6. The horizontal offset(x axis), will be updated when the `selectedTab` changes, so the light `moves` when the user switches tabs.
7. 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:**

![Light TabView](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j8m7awzdxtjt6l52za31.gif)

**It gets better on dark mode:**

![Dark TabView](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ntr4sezex1yhaxkfazyp.gif)



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. 🚀