Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIAlertController - Action not executed if alert dismissed the first time

In a project I'm working on, I had to write a UIAlert helper module that would display popups here and there in my iOS app. The popups are written as class functions that I can simply call anywhere in the code (the class is static and so are all the functions).

I am now encountering a very weird bug where if you dismiss an alert once, then open it again, its actions don't function anymore (as in, the action handler is not called). It does work if you click the action the first time the popup is displayed, though...

Here is the code for the specific popup for which this bug occurs (no other popups are affected whatsoever):

static func popSkipWalkthrough() {
    let alert = UIAlertController(title: "Skip", message: "whatever", preferredStyle: .Alert)

    alert.addAction(cancelAction)
    alert.addAction(skipWalkthroughAction)
    appDelegate.window!.rootViewController!.presentViewController(alert, animated: true, completion: nil)
}

The skipWalkthroughAction is defined as following:

static let skipWalkthroughAction = UIAlertAction(title: "Continue", style: .Default, handler: { (action: UIAlertAction!) -> Void in
    appDelegate.setWindowViewTo("NavCtrl", navigateTo: false)
    CallIn.Settings.didWalkthrough = true
})

And the cancelAction is defined as :

static let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)

This popup in particular is shown every time you press the 'skip' button in the last step of a walk-through...

I have tried a few leads on what the cause for this behavior is, and I think it might have something to do with the popup not really being deallocated, but I'm not sure at all at this point...

Any ideas ?

like image 250
Skwiggs Avatar asked Feb 09 '16 11:02

Skwiggs


1 Answers

All though I have problems with the way this reusable piece is coded, this problem can be solved by sending a copy: message to skipWalkthroughAction. Simply do a:

static func popSkipWalkthrough() {
    let alert = UIAlertController(title: "Skip", message: "whatever", preferredStyle: .Alert)

    alert.addAction(cancelAction.copy() as! UIAlertAction)
    alert.addAction(skipWalkthroughAction.copy() as! UIAlertAction)
    appDelegate.window!.rootViewController!.presentViewController(alert, animated: true, completion: nil)
}

This should solve it.

You can also solve this by moving the alert to instance level. You don't have to send copy: then.

Better approach

If you want a "truly" reusable experience of UIAlertController, you are better off creating a UIViewController extension. I have this in one of my projects :

extension UIViewController {
    func showAlertControllerWithTitle(title:String?,message:String?,actions:[UIAlertAction],dismissingActionTitle:String?, dismissBlock:(() -> ())?) -> UIAlertController {
        let alertController = UIAlertController(title: title, message: message, preferredStyle: .Alert)
        if dismissingActionTitle != nil {
            let okAction = UIAlertAction(title: dismissingActionTitle, style: .Default) { (action) -> Void in
                dismissBlock?()
                alertController.dismissViewControllerAnimated(true, completion:nil)
            }
            alertController.addAction(okAction)
        }
        for action in actions {
            alertController.addAction(action)
        }
        self.presentViewController(alertController, animated: true, completion:nil)
        return alertController
    }
}
like image 200
avismara Avatar answered Nov 09 '22 00:11

avismara