I'm using a custom tabview to show 6 tabs in the tabview. It's only limited to 5 so if you add 6, it creates a "More" section and the 5th and 6th tabs are shown in a navigation view. When you view 5th,6th tabs, it shows a back navigation bar on top with "more". I got the tabview to show all 6 tabs without "more" option but when I view 5th, 6th tabs it still show a navigation bar on top with "more" back button. X has implemented this with 6th tabs without the extra navigation bar on the 5,6 tabs, so it's certainly possible.
Ideally I'm trying to hide the "more "navigation bar from the first view and keep the navigation bar of the actual view.
I have tried
View6()
.tag(Tab.home5)
.navigationBarTitle("")
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
Tabview
VStack(spacing: 0){
TabView(selection: $currentTab) {
View1()
.tag(Tab.home1)
View2()
.tag(Tab.home2)
View3()
.tag(Tab.home3)
View4()
.tag(Tab.home4)
View5()
.tag(Tab.home5)
View6()
.tag(Tab.home6)
}
TabBar()
.toolbar(.hidden, for: .tabBar)
}
Custom Tab builder (shows all 6 tabs instead of a more section on the 5th tab)
@ViewBuilder
func TabBar()->some View{
HStack(spacing: 0){
ForEach(Icons){icon in
let tabColor: SwiftUI.Color = currentTab == icon.tabIcon ? (scheme == .dark ? .white : .black) : .gray.opacity(0.6)
ResizableIconView(IconView: icon.View,color: tabColor)
.aspectRatio(contentMode: .fit)
.frame(width: 30, height: 30)
.frame(maxWidth: .infinity)
.contentShape(Rectangle())
}
}
}
.padding(.horizontal)
.padding(.vertical,10)
.background {
(scheme == .dark ? Color.black : Color.white)
.ignoresSafeArea()
}
}
When I view 5th,6th tabs, extra more navigation is shown as below, more navigation bar
I tried setting navigationBarBackButtonHidden to true but it doesn't work. I've also tried hiding the navigation bar globally but this removes all navigation bars from alll tabs.
extension UINavigationController {
open override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
navigationBar.isHidden = true
}
}
I think the solution is UIKit related
Yes, the answer is basically UIKit. You should wrap a UITabBarController
and set its moreNavigationController.isNavigationBarHidden
to true.
I have combined this answer that shows a simple wrapper around a UITabBarController
with this answer about the correct timing to set isNavigationBarHidden
:
class CustomTabBarController: UITabBarController, UINavigationControllerDelegate {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
moreNavigationController.delegate = self
}
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
// hide the nav bar here:
navigationController.isNavigationBarHidden = true
}
}
struct TabBarController: UIViewControllerRepresentable {
var controllers: [UIViewController]
@Binding var switchTab: Int
func makeUIViewController(context: Context) -> CustomTabBarController {
let tabBarController = CustomTabBarController()
tabBarController.tabBar.isHidden = true // hide the tab bar here
tabBarController.viewControllers = controllers
tabBarController.delegate = context.coordinator
return tabBarController
}
func updateUIViewController(_ uiViewController: CustomTabBarController, context: Context) {
uiViewController.selectedIndex = switchTab
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, UITabBarControllerDelegate {
let owner: TabBarController
init(_ owner: TabBarController) {
self.owner = owner
}
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
owner.switchTab = tabBarController.selectedIndex
}
}
}
@MainActor
struct UIKitStyleTabView: View {
var viewControllers: [UIHostingController<AnyView>]
@Binding var switchTab: Int
struct Tab {
var view: AnyView
var barItem: UITabBarItem
init<V: View>(view: V, barItem: UITabBarItem) {
self.view = AnyView(view)
self.barItem = barItem
}
}
init(_ tabs: [Tab], switchTab: Binding<Int>) {
self.viewControllers = tabs.map {
let host = UIHostingController(rootView: $0.view)
host.tabBarItem = $0.barItem
return host
}
self._switchTab = switchTab
}
var body: some View {
TabBarController(controllers: viewControllers, switchTab: $switchTab)
.edgesIgnoringSafeArea(.all)
}
}
Example usage - this shows a 6-tabbed tab view, with a button in the first tab that brings you to the 6th tab.
struct ContentView : View {
@State var switchTab: Int = 0
let tabBarSymbolConfig = UIImage.SymbolConfiguration(scale: .medium)
var body : some View {
UIKitStyleTabView([
UIKitStyleTabView.Tab(view: Button("Go to tab 6") { self.switchTab = 5 }, barItem: UITabBarItem(title: "Social", image: UIImage(systemName: "person.3.fill", withConfiguration: tabBarSymbolConfig)!.withBaselineOffset(fromBottom: 4.5), selectedImage: nil)
), UIKitStyleTabView.Tab(view: Text("SearchView"), barItem: UITabBarItem(title: "Search", image: UIImage(systemName: "magnifyingglass", withConfiguration: tabBarSymbolConfig)!.withBaselineOffset(fromBottom: 4.5), selectedImage: nil)
), UIKitStyleTabView.Tab(view: Text("WorkoutsView"),barItem: UITabBarItem(title: "Workouts", image: UIImage(systemName: "doc.on.clipboard.fill", withConfiguration: tabBarSymbolConfig)!.withBaselineOffset(fromBottom: 4.5), selectedImage: nil)
), UIKitStyleTabView.Tab(view: Text("ExercisesView"), barItem: UITabBarItem(title: "Exercises", image: UIImage(systemName: "sportscourt", withConfiguration: tabBarSymbolConfig)!.withBaselineOffset(fromBottom: 4.5), selectedImage: nil)
), UIKitStyleTabView.Tab(view: Text("AccountView"), barItem: UITabBarItem(title: "Me", image: UIImage(systemName: "person.crop.circle", withConfiguration: tabBarSymbolConfig)!.withBaselineOffset(fromBottom: 4.5), selectedImage: nil)
), UIKitStyleTabView.Tab(view: Text("SomeOtherView"), barItem: UITabBarItem(title: "Other", image: UIImage(systemName: "person.crop.circle", withConfiguration: tabBarSymbolConfig)!.withBaselineOffset(fromBottom: 4.5), selectedImage: nil)
)
], switchTab: $switchTab)
}
}
You can also use SwiftUI Introspect to get a UITabBarController
from a TabView
directly, but it can only introspect on the versions you specify, so it is not future-proof at all.
TabView(selection: $tab) {
ForEach(0..<7) { i in
Text("Tab \(i)").tabItem { Text("\(i)") }
.toolbar(.hidden, for: .tabBar)
}
}
.introspect(.tabView, on: .iOS(.v13, .v14, .v15, .v16, .v17)) { tabView in
tabView.moreNavigationController.isNavigationBarHidden = true
}