Tabman: a paging view controller for iOS with interactive indicator bars
™️ A powerful paging view controller with interactive indicator bars
At a glance
- What is it?
- Tabman is a Swift library from uias that wraps Pageboy in a configurable bar of buttons, badges and indicators. It suits UIKit apps that need a multi-page flow without writing their own paging controller.
- Who is it for?
- Adopt Tabman when your app is a UIKit project on iOS 14 or later and your navigation is a set of sibling pages behind a bar of titles, images or badges. Do not adopt it if you need SwiftUI-native navigation, or if you cannot accept that the bar's appearance is spread across four separate object graphs (view, layout, buttons, indicator) that you have to learn individually.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 65 days ago.
- What is it written in?
- Mainly Swift, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 25, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The UIKit gap Tabman fills
UIKit gives you UIPageViewController and leaves the rest to you. There is no built-in bar that tracks the scroll position, no indicator that stretches or compresses as you drag, and no notion of a bar item with a badge. Teams that need that combination usually end up writing a custom container view controller: a scroll view, a stack of child view controllers, a set of buttons, and a pile of gesture bookkeeping. Tabman packages that work as a subclass.
The target reader is an iOS engineer working in UIKit who has a screen with a fixed set of sibling pages. Think a profile with Posts, Media and Likes, or a product page with Details, Reviews and Shipping. The pages are known up front, they are peers, and the user moves between them by swiping or by tapping a label. Tabman is not a router and not a navigation stack. It does not manage deep links, and it does not decide what a page contains. It is the container and the bar, and nothing else.
The README states the library requires iOS 14 or above and is compatible with Swift 6 (Xcode 16+). That floor matters: if you still support iOS 13, this version is out of reach. The MIT licence means you can ship it in a closed-source app, subject to the usual attribution terms in the LICENSE file.
Pageboy underneath, and the four parts of a bar
Tabman is built on Pageboy, a separate page view controller library from the same author. The split is deliberate. Pageboy owns the paging behaviour: which child view controller is on screen, how a drag translates into a page transition, and what the current index is. Tabman owns the bar that reflects that state and lets the user drive it. TabmanViewController is the seam between the two, and it is a PageboyViewController subclass, which is why the README's example assigns self to self.dataSource and conforms to PageboyViewControllerDataSource.
A bar is not one object. The documentation breaks every TMBar into four functional areas. TMBarView is the root and holds background style and transition behaviour. TMBarLayout decides how buttons are arranged, including contentMode (.fit or .intrinsic), contentInset and alignment. TMBarButton covers the individual items, and TMBarButtonCollection lets you customize all of them at once. TMBarIndicator is the moving element that shows the current page.
That decomposition is the library's real design decision, and it cuts both ways. It means you can change the indicator's overscroll behaviour without touching the layout, and it means you can apply a tint to every button, present and future, with one closure. It also means a newcomer who wants to change the background colour has to know that the property lives on bar.background.style rather than on the bar itself. The learning curve is in the object graph, not in the API surface.
Installing Tabman with Swift Package Manager
The README documents one installation route: Swift Package Manager, integrated through Xcode. There is no CocoaPods or Carthage instruction in the README, and no command-line snippet, so the steps below follow the Xcode flow the README describes rather than a terminal invocation.
Add the package in Xcode using the repository URL, then import both modules in the file that declares your container. Tabman re-exports nothing from Pageboy, so both imports are needed.
import Tabman
import PageboyNext, declare a subclass and give it the pages it will host. The README's example keeps an array of plain UIViewController instances and assigns self as the data source in viewDidLoad.
class TabViewController: TabmanViewController {
private var viewControllers = [UIViewController(), UIViewController()]
override func viewDidLoad() {
super.viewDidLoad()
self.dataSource = self
let bar = TMBar.ButtonBar()
bar.layout.transitionStyle = .snap
addBar(bar, dataSource: self, at: .top)
}
}After that, conform to both data source protocols. PageboyViewControllerDataSource answers how many pages exist, which view controller belongs at each index, and which page to open on. TMBarDataSource answers what each bar item looks like.
extension TabViewController: PageboyViewControllerDataSource, TMBarDataSource {
func numberOfViewControllers(in pageboyViewController: PageboyViewController) -> Int {
return viewControllers.count
}
func viewController(for pageboyViewController: PageboyViewController,
at index: PageboyViewController.PageIndex) -> UIViewController? {
return viewControllers[index]
}
func defaultPage(for pageboyViewController: PageboyViewController) -> PageboyViewController.Page? {
return nil
}
func barItem(for bar: TMBar, at index: Int) -> TMBarItemable {
return TMBarItem(title: "Page \(index)")
}
}Run the app and you should see two pages you can swipe between, with a bar pinned to the top whose indicator follows the drag. The README notes that when adding a bar you can target the predefined areas .top, .bottom or .navigationItem(item:), or supply your own container with .custom(view:layout:).
Bar items, badges and the UIKit itemables that do not update
The bar asks for a TMBarItemable for each page. TMBarItem is the default implementation, and it carries a title, an image and a badgeValue. Setting badgeValue to a string is how you get the small numeric or text marker on a tab, and TMBadgeView is the view that renders it.
Tabman also accepts two native UIKit types as bar itemables: UINavigationItem and UITabBarItem. This is a genuine convenience when you already have those objects configured. It comes with a documented caveat the README states plainly: those types are unable to support dynamic updating of the bar when you set properties. In practice that means a UINavigationItem-backed bar will not refresh if you change its title after the bar has been built. If your bar items change at runtime, use TMBarItem or your own TMBarItemable conformance instead, and treat the UIKit bridging as a one-shot configuration path.
Customization of the buttons themselves goes through a closure that applies to existing buttons and to any added later. The README's example sets tintColor and selectedTintColor together:
bar.buttons.customize { (button) in
button.tintColor = .orange
button.selectedTintColor = .red
}That deferred-application behaviour is worth knowing before you write code that reaches for a button by index. The collection is the supported handle.
Where Tabman is the wrong tool
Tabman assumes a fixed, ordered set of pages. If your content is a feed with unknown length, or a hierarchy the user drills into, a paging bar is the wrong shape and Tabman will not help you fake it. The data source wants a count and an index-to-view-controller mapping, not an infinite sequence.
The second limit is platform. This is a UIKit library. It requires iOS 14 or above and Swift 6 compatibility is stated for Xcode 16 and later. A project built around SwiftUI navigation has no natural place to put a TabmanViewController without wrapping it in a UIViewControllerRepresentable, and the README does not document that path. If your app is SwiftUI-first, the cost of bridging probably exceeds the benefit.
The third limit is the customization model itself. Because a bar is four cooperating objects, deep visual changes mean learning four sets of properties and how they interact. A designer who wants a bar that does not resemble any of the TMBar templates will find the template list in TMBar+Templates.swift is a starting point, not a constraint, but also not a shortcut. The README does not document rollback or migration between major versions, so the CHANGELOG.md file in the repository is the place to look before a major upgrade.
Tabman compared with JXSegmentedView
JXSegmentedView is the other name that comes up in searches around this problem, and the two libraries make different bets. JXSegmentedView is a segmented control: a bar with an indicator, designed to sit above content you manage yourself. It does not own the paging. You pair it with a scroll view or a page controller and wire the two together, which gives you freedom in how pages are hosted and responsibility for keeping the indicator and the content in sync.
Tabman takes the opposite approach by owning the container. TabmanViewController is a PageboyViewController subclass, so the bar and the pages are one object and the synchronisation is internal. You give up control over how pages are hosted and you get the indicator tracking for free. Which is better depends on whether your pages are already hosted by something else. If you have an existing container you cannot replace, JXSegmentedView's bar-only design fits more easily. If you are starting the screen from scratch, Tabman's integrated container removes a class of bugs where the bar and the content disagree about the current index.
The other difference is dependency shape. Tabman brings Pageboy with it, which is visible in Package.swift and Package.resolved. JXSegmentedView does not impose a page controller. Neither is a superset of the other.
Maintenance, versioning and upgrade cost
The repository is not archived, and the last push was on 2026-07-27. The most recent release is 4.0.1, published on 2026-04-28, following 4.0.0 on 2025-12-28 and 3.2.0 on 2024-04-14. The gap between 3.2.0 and 4.0.0 is roughly twenty months, so the major line moves slowly. That is reassuring for stability and less so if you are waiting on a fix.
Upgrade cost concentrates in the major versions. The jump to 4.0.0 raised the platform floor to iOS 14 and aligned with Swift 6 and Xcode 16. If you are on 3.x, that is a real migration, not a patch bump. The repository ships a CHANGELOG.md at the top level, and reading it before bumping the version in your package manifest is the cheapest step available.
The MIT licence permits commercial and closed-source use. The practical obligations are the ones in the LICENSE file: keep the copyright notice and the permission notice with any copy or substantial portion of the software. That is a distribution detail, not a design constraint, and it does not affect how you structure your app. It is not legal advice; read the file yourself.
Editorial conclusion
Adopt Tabman when your app is a UIKit project on iOS 14 or later and your navigation is a set of sibling pages behind a bar of titles, images or badges. Do not adopt it if you need SwiftUI-native navigation, or if you cannot accept that the bar's appearance is spread across four separate object graphs (view, layout, buttons, indicator) that you have to learn individually. Before committing, check Package.swift for the exact Pageboy version your build will resolve, and confirm the TMBar template you want exists in TMBar+Templates.swift rather than assuming you can style it from scratch.
Frequently asked questions
How do I install Tabman in an iOS project?
The README documents Swift Package Manager as the installation route, integrated through Xcode. There are no CocoaPods or Carthage instructions in the README, so the package manager path is the supported one.
What iOS version does Tabman require?
The README states that Tabman requires iOS 14 or above and is compatible with Swift 6 (Xcode 16+). Projects supporting earlier iOS versions cannot use the current release line.
Can I use UINavigationItem or UITabBarItem as Tabman bar items?
Yes, Tabman supports both as TMBarItemable types. The README notes that these types cannot support dynamic updating of the bar when you set properties, so runtime changes need TMBarItem or a custom conformance instead.
Does Tabman handle the page content itself?
No. Tabman is built on Pageboy, which owns the paging behaviour, and Tabman supplies the bar that reflects and drives it. You provide the view controllers through the PageboyViewControllerDataSource methods.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/uias-tabman)