Reading time: 2 min

This recipe shows how to style a TabView in SwiftUI - change its background color, text and icon colors and styles, as well as changing the badge coloring. This solution works on all SwiftUI and iOS versions. The end result looks like this:

preview

The recipe goes as follows:

  1. Set icon, text and badge colors using UITabBarItemAppearance.
  2. Set background color in UITabBarAppearance.
  3. Assign the styled item appearance to bar appearance.
  4. Set the styled bar appearance as the standardAppearance and scrollEdgeAppearance of UITabBar.

First, add this extension for converting a Color to a UIColor, since UIKit only works with UIColor:

extension Color {
  var uiColor: UIColor? {
    if #available(iOS 14.0, *) {
      return UIColor(self)
    } else {
      let scanner = Scanner(string: self.description.trimmingCharacters(in: CharacterSet.alphanumerics.inverted))
      var hexNumber: UInt64 = 0
      var r: CGFloat = 0.0, g: CGFloat = 0.0, b: CGFloat = 0.0, a: CGFloat = 0.0
      let result = scanner.scanHexInt64(&hexNumber)
      if result {
        r = CGFloat((hexNumber & 0xff000000) >> 24) / 255
        g = CGFloat((hexNumber & 0x00ff0000) >> 16) / 255
        b = CGFloat((hexNumber & 0x0000ff00) >> 8) / 255
        a = CGFloat(hexNumber & 0x000000ff) / 255
        return UIColor(red: r, green: g, blue: b, alpha: a)
      } else {
        return nil
      }       
    }
  }
}

Then, apply the recipe above for styling the tab bar:

extension View {
  func tabViewStyle(backgroundColor: Color? = nil,
                    itemColor: Color? = nil,
                    selectedItemColor: Color? = nil,
                    badgeColor: Color? = nil) -> some View {
    onAppear {
      let itemAppearance = UITabBarItemAppearance()
      if let uiItemColor = itemColor?.uiColor {
        itemAppearance.normal.iconColor = uiItemColor
        itemAppearance.normal.titleTextAttributes = [
          .foregroundColor: uiItemColor
        ]
      }
      if let uiSelectedItemColor = selectedItemColor?.uiColor {
        itemAppearance.selected.iconColor = uiSelectedItemColor
        itemAppearance.selected.titleTextAttributes = [
          .foregroundColor: uiSelectedItemColor
        ]
      }
      if let uiBadgeColor = badgeColor?.uiColor {
        itemAppearance.normal.badgeBackgroundColor = uiBadgeColor
        itemAppearance.selected.badgeBackgroundColor = uiBadgeColor
      }

      let appearance = UITabBarAppearance()
      if let uiBackgroundColor = backgroundColor?.uiColor {
        appearance.backgroundColor = uiBackgroundColor
      }

      appearance.stackedLayoutAppearance = itemAppearance
      appearance.inlineLayoutAppearance = itemAppearance
      appearance.compactInlineLayoutAppearance = itemAppearance

      UITabBar.appearance().standardAppearance = appearance
      if #available(iOS 15.0, *) {
        UITabBar.appearance().scrollEdgeAppearance = appearance
      }
    }
  }
}

Great! Now you can apply it to any TabView:

TabView {
  Text("Tab 1")
    .tabItem {
      Label("SwiftUI 1 & 2", systemImage: "arrowshape.turn.up.backward")
    }
  Text("Tab 2")
    .tabItem {
      Label("SwiftUI 3", systemImage: "arrowshape.turn.up.forward")
    }
    .badge(10) // only since iOS 15
}
.tabViewStyle(backgroundColor: .blue.opacity(0.3),
              itemColor: .orange.opacity(0.95),
              selectedItemColor: .red,
              badgeColor: .green)
Additional styling options

Having direct access to UITabBarAppearance and UITabBarItemAppearance gives you a lot of additional styling options. Here are a few highlights:

Apply blur effects to bar background:

appearance.backgroundEffect = .init(style: .systemThickMaterialDark)

Screenshot%202021-08-25%20at%2009.12.03

Set bar background image:

appearance.backgroundImage = UIImage(named: "grass")

Screenshot%202021-08-25%20at%2009.15.21

Change title font:

itemAppearance.normal.titleTextAttributes = [
  .foregroundColor: uiItemColor,
  .font: UIFont(name: "Chalkduster", size: 18)
]

Screenshot%202021-08-25%20at%2009.18.47

Next Post Previous Post