Search code examples
iosswiftuikituitabbarcontrolleruitabbaritem

How to Add a Child ViewController to UITabBarController Without Creating a "Ghost" Tab Bar Item in iOS?


I'm working on an iOS app where I need to present a view controller over the entire screen, including a UITabBarController, without adding the view controller to the tab bar's items or altering the tab bar's functionality. My goal is to overlay this view controller without making it one of the selectable tabs, thus maintaining the integrity of the user interface.

Here's the challenge: When I use addChild and addSubview methods to add the view controller to the tab bar controller, it disrupts the existing tab bar items. Alternatively, when presenting modally, I achieve the overlay effect I want but struggle with other modal-related behaviors.

Here are the two approaches I've tried: Adding as a child:

func presentAdditionalViewController(
    from presentingController: UIViewController,
    to tabBarController: UITabBarController
) {
    let additionalViewController = UIViewController()

    DispatchQueue.main.async {
        presentingController.dismiss(animated: false) {
            tabBarController.addChild(additionalViewController)
            tabBarController.view.addSubview(additionalViewController.view)
            additionalViewController.didMove(toParent: tabBarController)
            additionalViewController.view.frame = tabBarController.view.frame
            tabBarController.view.bringSubviewToFront(additionalViewController.view)
        }
    }
}

This method results in a "ghost" tab bar item appearing, which is not desirable.

So I tried use alternative method 2. Modal presentation:

func presentAdditionalViewController(from presentingController: UIViewController, in tabBarController: UITabBarController) {
    let additionalViewController = UIViewController()  // Placeholder for the actual view controller

    // Setup modal presentation styles
    additionalViewController.modalPresentationStyle = .overFullScreen
    additionalViewController.modalTransitionStyle = .crossDissolve

    DispatchQueue.main.async {
        presentingController.dismiss(animated: false) {
            tabBarController.present(additionalViewController, animated: true, completion: nil)
        }
    }
}

So this method worked, in a sense that it did not add another tab. But the thing is that, I want this "additional child view controller" to be floating just over the tabbar, meaning user should be able to interact with the tabbar, switching tabs, playing around with the views behind "additional child viewcontroller".

But the 2nd method somehow leaves this "UITransitionView" that blocks any sort of interaction.

Upon iOS18 update, I start to notice this "ghost" tabbar item, and I am struggling to resolve this, would appreciate some advice.


Solution

  • Create a containerView that holds the tabbar to circumvent the issue

    func presentAdditionalViewController(
        from presentingController: UIViewController,
        to homeContainerViewController: UIViewController
    ) {
        let additionalViewController = UIViewController()
    
        DispatchQueue.main.async {
            presentingController.dismiss(animated: false) {
                homeContainerViewController.addChild(additionalViewController)
                homeContainerViewController.view.addSubview(additionalViewController.view)
                additionalViewController.didMove(toParent: homeContainerViewController)
                additionalViewController.view.frame = homeContainerViewController.view.frame
                homeContainerViewController.view.bringSubviewToFront(additionalViewController.view)
            }
        }
    }