The following solution involves creating a custom UITabBarController
subclass. This custom class saves off the controller's last view controller and replaces it with an empty view controller. Then it intercepts a check if a tab should be selected. If the last tab is being selected then the selection is rejected and then the last view controller is presented modally.
class SpecialTabBarController: UITabBarController {
private var lastViewController: UIViewController?
// When the view controllers are set, replace the last view controller
// with a dummy controller (so the tab appears normally). Save off the
// the real last view controller for modal presentation when needed.
override var viewControllers: [UIViewController]? {
didSet {
if let viewControllers, viewControllers.count > 1 {
lastViewController = viewControllers.last
var newVCs = Array(viewControllers.dropLast())
let extra = UIViewController()
extra.tabBarItem = lastViewController?.tabBarItem
newVCs.append(extra)
super.viewControllers = newVCs
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
delegate = self
if let vcs = viewControllers {
viewControllers = vcs
}
}
}
extension SpecialTabBarController: UITabBarControllerDelegate {
// When a tab is tapped, this is called to confirm whether the
// corresponding controller should be shown. Here we disable the
// showing of the last view controller and then manually present it.
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if let lastViewController, viewController === viewControllers?.last {
present(lastViewController, animated: true)
return false
} else {
return true
}
}
}
This solution will work with a programmatically created tab bar controller as well as with one created in a storyboard. To work in a storyboard, update the tab bar controller's class from the default of UITabBarController
to SpecialTabBarController
. That's the only change to make this work.
This solution does cause one limitation. Since SpecialTabBarController
makes itself its own UITabBarControllerDelegate
, you can't make any other object the delegate. Though there are solutions to this if needed. That's left as an exercise for the reader.
For the dialog you want in the center of the last view controller (VC4), you can have the code for that view controller display a UIAlertController
when it appears.