StatefulTabView
  • August 19, 2025

A SwiftUI UITabBarController implementation that retains state between tab changes. Big thanks to Amzd and everyone who helped to refine this gist as it was a major jumping off point for setting up this project.

Requirements


  • iOS 13.0+
  • Xcode 11.2+
  • Swift 5+

Installation


Swift Package Manager

In Xcode 11 or greater, navigate to File > Swift Packages > Add Package Dependency.... From there just simply add https://github.com/NicholasBellucci/StatefulTabView as the package repository url and use the master branch or the most recent version. Master will always be inline with the newest release.

Features


  •  State driven selected index
  •  TabBar appearance configuration
  •  TabBar custom tint color
  •  TabBar custom background color
  •  TabBarItem custom title and image
  •  TabBarItem badge value
  •  State retention from tab to tab
  •  Pop to root functionality when selecting the already selected tab
  •  Scroll to top functionality when selecting the already selected tab at the root view

Usage


Setting up StatefulTabView is relatively simple and works similar to the native TabView. The main difference is that the content of the tabs is wrapped in a Tab struct. There is no limitation on how many tabs can be used. Once more than 5 are used, the Apple standard more tab will become available. Feel free to check out the example project for the exact usage.

Basic

StatefulTabView {
Tab(title: “Tab 1”, systemImageName: “circle.fill”) {
NavigationView {
List {
Section {
ForEach(0..<20, id: \.self) { index in
NavigationLink(destination: PushedView(text: “Pushed number \(index)”)) {
Text(“\(index)”)
}
}
}
}
.navigationBarTitle(“Navigation View 1”)
}
}
}

Appearance Modifications

All appearance modifications can be made by using extensions for the StatefulTabView.

StatefulTabView {

}
.barTintColor(.red)
.unselectedItemTintColor(.green)
.barBackgroundColor(.yellow)
.barAppearanceConfiguration(.transparent)

Selected Index

The selected index of the StatefulTabView can be set within the initializer. The passed value is a binding.

@State var selectedIndex: Int = 2

StatefulTabView(selectedIndex: $selectedIndex) {

}

Badge Value

The TabBarItem badge value can be set in the initializer of a Tab.

@State var badgeValue: String = “1”

Tab(title: “Tab 1”, systemImageName: “circle.fill”, badgeValue: badgeValue) {

}

Scroll to Top with Large Titles

Scroll to top is handled when selecting the already selected tab that contains a scrollView in the heirarchy. The only issue is that large titles in navigation bars are not factored in when calling scrollRectToVisible(CGRect(x: 0, y: 0, width: 1, height: 1), animated: true) for obvious reasons. Due to this limitation adding .prefersLargeTitle(true) to a Tab will fix this issue. For root navigation views that do not use a large title no change to a Tab is needed.

Tab(title: “Tab 1”, systemImageName: “circle.fill”) {
NavigationView {
List {

}
.navigationBarTitle(“Navigation View 1”, displayMode: .large)
}
}
.prefersLargeTitle(true)

GitHub


View Github

YOU MIGHT ALSO LIKE...
MijickPopups Hero

  Popups Alerts Resizable Sheets Banners

SwiftUI Tooltip

This package provides you with an easy way to show tooltips over any SwiftUI view, since Apple does not provide ...

SimpleToast for SwiftUI

SimpleToast is a simple, lightweight, flexible and easy to use library to show toasts / popup notifications inside iOS or ...

SSToastMessage

Create Toast Views with Minimal Effort in SwiftUI Using SSToastMessage. SSToastMessage enables you to effortlessly add toast notifications, alerts, and ...

ToastUI

A simple way to show toast in SwiftUI   Getting Started • Documentation • Change Log