1

I’m trying to create more than 5 tabs in a UITabBarController on iOS 26. By default, the 6th tab and onwards get grouped into the “More” tab.

I’ve already tried using the regular trait collection approach (checking size classes and setting tabs accordingly), but it still doesn’t allow me to show more than 5 tabs directly in the tab bar.

Is there a way to: • Increase the number of visible tabs in the tab bar (e.g., 6+), OR • Override this default “More” behavior?

Any guidance or workaround would be really helpful.

4
  • This goes against the Apple's Human Interface Guidelines. Short answer there is no way to override this behavior. tabbars Commented Sep 29 at 3:43
  • 1
    @LeoDabus Which line in the HIG does this go against? The closest I can see would be "use the appropriate number of tabs", but without further information, we don't really know if 6 tabs is "appropriate" for OP's app. On the other hand, HIG also says "avoid overflow tabs." So avoiding the "more" tab is in fact encouraged by the HIG. Commented Sep 29 at 3:55
  • @Sweeper "it’s important to weigh the complexity of additional tabs against the need for people to frequently access each section; keep in mind that it’s generally easier to navigate among fewer tabs. " besides that if you increase the number of tabs they get too small to tap. Commented Sep 29 at 6:32
  • 1
    You can make your own, but it will be so ugly like this Commented Oct 3 at 14:38

2 Answers 2

1

It's in UITabBarController's docs:

The tab bar has limited space for displaying your custom items. If you add six or more custom view controllers to a tab bar controller, the tab bar controller displays only the first four items plus the standard More item on the tab bar. Tapping the More item brings up a standard interface for selecting the remaining items.

So, I don't think it's possible to override this behaviour. If you really need to, I suppose you'll have to implement your own UI component instead.

Sign up to request clarification or add additional context in comments.

2 Comments

we can show more tabs by providing horizontal size classes. See the reference but this does not work in iOS 26, it only work for iOS prior to iOS 26. stackoverflow.com/questions/54832621/…
I didn't make up the quote from docs: Apple explicitly mentions "six or more." It's hard to speculate about the internals of UIKit components, though. Maybe they just fixed this traitCollection loophole in iOS 26 to match the behaviour they wanted. In any case, I think you should treat official docs as a single source of truth and implement your own UI component, if the standard one doesn't behave as you want it to.
-1

So the limit of 6 tabs is enforced by the UITabBarController() I believe. I could not find a way to amend this limit. A lone instance of a UITabBar() however, will not place any tabs in a more tab, and will allow the developer to break the UI if so desired. My plan is to just implement the UITabBar() , and trust the developer to ensure that each tab has the recommended minimum frame of 44x44 according to the HIG.

My code is based around enums because I find them convenient.

First I created a struct, TabIcon, to collect the icon data:

public struct TabIcon {
  let title : String?
  let icon  : UIImage?

  public init ( title      : String  , systemName: String ) { self.title = title   ;   self.icon = UIImage ( systemName: systemName )  }
  public init ( systemName : String                       ) { self.title = nil     ;   self.icon = UIImage ( systemName: systemName )  }
  public init ( title      : String                       ) { self.title = title   ;   self.icon = nil                                 }
}

Then I implemented the protocol, TabOption. Designed to be placed on enums:

public protocol TabOption: RawRepresentable , CaseIterable , Hashable , View where Self.RawValue == Int {
  static var home: Self { get }
  var tab:  TabIcon { get }
}

( Notice it conforms to View. )
Each case of the enum is potential Tab that can be navigated to.

I ran an extension off of the protocol to extract a UITabBarItem out of each case of the enum.

fileprivate extension TabOption {
   var tabItem: UITabBarItem {
     UITabBarItem ( title: self.tab.title , image: self.tab.icon , tag: self.rawValue )
  }
}

And finally, I created the UIViewRepresentable() responsible for implementing UITabBar() :

public struct CustomTabBar < Case: TabOption >: UIViewRepresentable {
  @Binding var selection: Case
  
  let items: [ UITabBarItem ]
  
  public init ( selection: Binding < Case > ) {
    self._selection = selection
    self.items = Case.allCases.map { $0.tabItem }
  }

  public func makeUIView ( context: Context ) -> UITabBar {
    let tabBar = UITabBar()
    tabBar.items = items
    tabBar.selectedItem = items [ selection.rawValue ]
    tabBar.delegate = context.coordinator
    return tabBar
  }

  public func updateUIView ( _ uiView: UITabBar , context: Context ) { }

  public func makeCoordinator() -> Coordinator { Coordinator ( $selection ) }

  public class Coordinator: NSObject , UITabBarDelegate {
    @Binding var selection: Case
    init ( _ selection: Binding < Case > ) { self._selection = selection }

    public func tabBar ( _ tabBar: UITabBar , didSelect item: UITabBarItem ) {
      selection = Case ( rawValue: item.tag ) ?? .home
    }
  }
}

It binds to a single instance of the protocol, and creates the TabBar() ( which has no limit on tabs. )

For Testing, I created an enum:

public enum Tab: Int , TabOption {
  case home , two , three , four , five , six

  public var tab: TabIcon {
    switch self {
    case .home:  TabIcon ( title: "One"      ,  systemName: "1.circle" )
    case .two:   TabIcon ( title: "Two"      ,  systemName: "2.circle" )
    case .three: TabIcon ( title: "three"    ,  systemName: "3.circle" )
    case .four:  TabIcon ( title: "four"     ,  systemName: "4.circle" )
    case .five:  TabIcon ( title: "settings" ,  systemName: "5.circle" )
    case .six:   TabIcon ( title: "more"     ,  systemName: "6.circle" )
    }
  }
  public var body: some View {
    switch self {
    case .home   : Text ( "one" )
    case .two    : Image ( systemName: "star.fill" ).resizable().frame ( width: 70 , height: 70 )
    case .three  : Circle().fill ( .red )
    case .four   : Circle()
    case .five   : RoundedRectangle ( cornerRadius: 30 ).fill ( .blue ).padding ( 30 )
    case .six    : Rectangle()
    }
  }
}

It conforms to theTabOption protocol, is a view , and has a TabIcon value for each case.

I created a convenience struct that implements the view for the CustomTabView.

fileprivate struct CustomTabView < Case: TabOption > : View {
  @State var selection: Case = .home
  var body: some View {
    VStack ( spacing: 0 ) {
      self.selection .frame ( maxHeight: .infinity , alignment: .center )
      CustomTabBar ( selection: $selection )
    }
    .ignoresSafeArea ( edges: .bottom )
  }
}

And then for ultimate convenience, I implement an extension on the protocol calling the CustomTabView.

public extension TabOption {
  static var tabView: some View { CustomTabView < Self > () }
}

Best Regards:

struct ContentView: View {
  var body: some View {
    Tab.tabView
  }
}

2 Comments

The question is tagged UIKit, not SwiftUI. Please post appropriate answers.
It ask for “guidance or workarounds.”

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.