Mikhail Krasnoselskiy
Mikhail Krasnoselskiy

Reputation: 198

How to display an Alert before leaving current ViewController in TabBarController?

I'm trying to remind user to save changes on current View Controller

let's say, i've got something like this:

enter image description here

here, inside TabBarController, and inside Navigation Controller I've got a "Favorites" tab. I want to display an alert, if user switches to "Contacts"

The issue is that Alert is shown over destination ViewController (Contacts), so it looks very weird for user.

Tested solutions:

first, i tried to use

override func viewWillDisappear(animated: Bool) {
    self.leavingAlert()
}
//inside FavoritesViewController

next, I tried:

class FavoritesViewController: UIViewController, UITabBarControllerDelegate {

override func viewDidLoad() {
    super.viewDidLoad()

    self.tabBarController?.delegate = self
}

func leavingAlert(){
    let alert = UIAlertController(title: "Alert", message: "You forgot to do something here", preferredStyle: UIAlertControllerStyle.Alert)
    let alertAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil)
    alert.addAction(alertAction)
    self.presentViewController(alert, animated: true, completion: nil)
}

func tabBarController(tabBarController: UITabBarController, didSelectViewController viewController: UIViewController) {
    self.leavingAlert()
}
}

Same effect

Then, i tried to reach the event through the TabBarViewController:

class TabBarViewController: UITabBarController {

override func viewDidLoad() {
    super.viewDidLoad()

}

    override func tabBar(tabBar: UITabBar, didSelectItem item: UITabBarItem) {
        if let navigationController = selectedViewController as? UINavigationController {
            if let activeController = navigationController.visibleViewController as? FavoritesViewController {
                activeController.leavingAlert()
            }
        }
    }
}

And one more time - same effect.

Note, that I'm not going to interrupt this UITabBarController Segue. The idea is only to ask "Save or not to save?", and if "save", then do some stuff and continue switching tab, if "don't save" - switch the tab immediately.

Thank you for any help. If there is a solution in Obj-C, please also answer, i'll try to catch the idea.

Upvotes: 2

Views: 2116

Answers (3)

André Slotta
André Slotta

Reputation: 14030

you should subclass your UITabBarController and let it be its own delegate. something like this should work:

class TabBarController: UITabBarController {
    var viewControllerToSelect: UIViewController?

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        delegate = self
    }

    func showLeavingAlert() {
        let leavingAlert = UIAlertController(title: "Warning", message: "Do you want to save before you leave?", preferredStyle: .Alert)

        let saveAction = UIAlertAction(title: "Yes", style: .Default) { (action) in
            let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))
            dispatch_after(delayTime, dispatch_get_main_queue()) {
                // switch viewcontroller after one second delay (faked save action)
                self.performSwitch()
            }
        }
        leavingAlert.addAction(saveAction)

        let cancelAction = UIAlertAction(title: "No", style: .Cancel) { (action) in
            // switch viewcontroller immediately
            self.performSwitch()
        }
        leavingAlert.addAction(cancelAction)

        presentViewController(leavingAlert, animated: true, completion: nil)
    }

    func performSwitch() {
        if let viewControllerToSelect = viewControllerToSelect {
            // switch viewcontroller immediately
            selectedViewController = viewControllerToSelect
            // reset reference
            self.viewControllerToSelect = nil
        }
    }
}

extension TabBarController: UITabBarControllerDelegate {
    func tabBarController(tabBarController: UITabBarController, shouldSelectViewController viewController: UIViewController) -> Bool {
        if let navigationController = selectedViewController as? UINavigationController, _ = navigationController.visibleViewController as? FavoritesViewController {
            // save a reference to the viewcontroller the user wants to switch to
            viewControllerToSelect = viewController

            // present the alert
            showLeavingAlert()

            // return false so that the tabs do not get switched immediately
            return false
        }

        return true
    }
}

Upvotes: 1

Andrew Romanov
Andrew Romanov

Reputation: 5066

You can create delegate for UITabBarController, and overload method:

optional func tabBarController(_ tabBarController: UITabBarController,
    shouldSelectViewController viewController: UIViewController) -> Bool

If user tried switch to the viewController (from FavoritesViewController) you return false from this method and show the alert.
In one of callbacks of the alert you can switch to destination programmatically.

unowned(unsafe) var selectedViewController: UIViewController?

Upvotes: 2

heximal
heximal

Reputation: 10517

One of solution is to replace segue with regular IBAction. your button Contacts must invoke IBAction on 'touch up inside' event where you display an alert and then in alert completion handler you call performSegueWithIdentifier method of your controller.

Upvotes: 0

Related Questions