7

I'm working on a side project with SwiftUI, and I have a scenario where I need to show a badge number on one of the tabItems of my TabView.

I have everything set with my models, and binding and so on. But I have been looking for 2 days now to find a modifier to show the badge, but unfortunately all my researches had faced a dead end.

I had went through Apple SwiftUI API Documentation for TabView and searched here on stack overflow and also of course tons of google search results' links.

I'm observing an environment object which hold a list of items and I know how to bind the badge with my items count, so when I have any item update (added, deleted) the badge will be updated. But I'm unable to find a way to show the badge number itself on the tabItem.

Any help is very appreciated!

JihadiOS
  • 381
  • 2
  • 13
  • 1
    SwiftUI 3 (requiring iOS 15) now has a `.badge` modifier. [See this answer for an example.](https://stackoverflow.com/a/67894115/77567) – rob mayoff Jun 08 '21 at 20:24

2 Answers2

10

iOS 15+

We can now use a native badge modifier that is available in list rows and tab bars:

TabView {
    Text("Tab One")
        .tabItem {
            Text("Home")
            Image(systemName: "house")
        }
        .badge(3)
}
pawello2222
  • 46,897
  • 22
  • 145
  • 209
9

There are no modifiers for setting badge number on tab item and If you try to add custom view for that, it won't be visible; This is from TabView documentation:

Tab views only support tab items of type Text, Image, or an image followed by text. Passing any other type of view results in a visible but empty tab item.

So, I tried a workaround and it works. Using ZStack with GeometryReader and some calculations to place the custom badge view in the right place.

struct ContentView: View {
  @State private var badgeNumber: Int = 3
  private var badgePosition: CGFloat = 3
  private var tabsCount: CGFloat = 3

  var body: some View {
    GeometryReader { geometry in
      ZStack(alignment: .bottomLeading) {
        // TabView
        TabView {
          Text("First View")
            .tabItem {
              Image(systemName: "1.circle")
              Text("First")
          }.tag(0)

          Text("Second View")
            .tabItem {
              Image(systemName: "2.circle")
              Text("Second")
          }.tag(1)

          Text("Third View")
            .tabItem {
              Image(systemName: "3.circle")
              Text("Third")
          }.tag(2)
        }

        // Badge View
        ZStack {
          Circle()
            .foregroundColor(.red)

          Text("\(self.badgeNumber)")
            .foregroundColor(.white)
            .font(Font.system(size: 12))
        }
        .frame(width: 20, height: 20)
        .offset(x: ( ( 2 * self.badgePosition) - 1 ) * ( geometry.size.width / ( 2 * self.tabsCount ) ), y: -30)
        .opacity(self.badgeNumber == 0 ? 0 : 1)
      }
    }
  }
}

The sample project available on github: https://github.com/aelzohry/TabBarSwiftUI

Ahmed Elzohry
  • 230
  • 3
  • 9
  • It worked like a charm, thanks! I had adjusted the badge appearance to fit my need. – JihadiOS Oct 28 '19 at 18:04
  • You're welcome. That's the power of Views in SwiftUI; you can adjust them as you like to fit your needs. – Ahmed Elzohry Oct 30 '19 at 19:39
  • 3
    This worked great until I added a text field to my view. When the keyboard opened and shifted the bottom of the view up, it brought the floating badge with it :/ – lmh Sep 11 '20 at 20:55
  • 4
    Note that with iOS 14, you'll need to add .ignoresSafeArea(.keyboard) to the enclosing ZStack to avoid the keyboard floating badge issue. – Curmudgeonlybumbly Sep 17 '20 at 17:37