Prevent Dismissal of Uialertcontroller

Prevent UIAlertController to dismiss

EDIT: Updated for Swift 5

EDIT: Updated to include @skywalker's feedback

So I actually got this to work. In short, it involves adding a long-press gesture recognizer to the UIAlertController that triggers before the dismissal occurs.

First, create lazily loaded computed variables in your view controller for your UIAlertController and the UIAlertAction you want to prevent from triggering so that self is accessible via the gesture recognizer's selector method you'll be attaching to the alert (self in the selector insinuates that all of this is inside a view controller).

lazy var alert: UIAlertController = {

let alert = UIAlertController(title: "Title", message: "Message", preferredStyle: .alert)

alert.addTextField(configurationHandler: nil)

let appendAction = self.appendAction
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)

alert.addAction(appendAction)
alert.addAction(cancelAction)

let gestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(append(sender:)))
gestureRecognizer.minimumPressDuration = 0.0
alert.view.addGestureRecognizer(gestureRecognizer)

return alert
}()

lazy var appendAction: UIAlertAction = {
return UIAlertAction(title: "Paste Message", style: .default, handler: nil)
}()

Make sure your gesture recognizer above is a UILongPressGestureRecognizer set with a minimum press duration of 0. That way you can access the state of the gesture (for when the user touches down) before the action is triggered fully. There you can disable the UIAlertAction, implement your custom code, and reenable the action after the gesture has completed (user has touched up). See below:

@objc func append(sender: UILongPressGestureRecognizer) {

switch sender.state {
case .began:
appendAction.isEnabled = false
case .ended:
// Do whatever you want with the alert text fields
print(alert.textFields?[0].text)
appendAction.isEnabled = true
default:
return
}
}

Also, make sure that the view controller owning the presentation of this alert conforms to UIGestureRecognizerDelegate in order to recognize simultaneous gestures.

extension YourViewController: UIGestureRecognizerDelegate {

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}

Then, just present the UIAlertController wherever.

func showAlert() {
self.present(alert, animated: true, completion: nil)
}

This is obviously a hack, but there's no other way that I know to achieve this without a hack since it's not meant to be achieved. For example, the gesture recognizer is tied to the UIAlertController so the user can trigger that method if they tap anywhere on the alert (besides the cancel button).

ORIGINAL ANSWER:

This is as close as I could come to a hack-a-round. If there was some way to customize the dismissal transition time to nothing then you could set animated: to false and it would look like the same alert, but I don't think it's possible

class ViewController: UIViewController {

@IBAction func alert(sender: AnyObject) {
let alert = UIAlertController(title: "title", message: "message", preferredStyle: .Alert)

alert.addTextFieldWithConfigurationHandler(nil)

let appendAction = UIAlertAction(title: "Append text", style: .Default) { _ in
var textField = alert.textFields![0] as UITextField
// Append text here
self.presentViewController(alert, animated: true, completion: nil)
}

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

alert.addAction(appendAction)
alert.addAction(cancelAction)

self.presentViewController(alert, animated: true, completion: nil)
}
}

I'm only familiar with swift

Prevent dismissal of UIAlertController

You're correct: if the user can tap a button in your alert, the alert will be dismissed. So you want to prevent the user from tapping the button! It's all just a matter of disabling your UIAlertAction buttons. If an alert action is disabled, the user can't tap it to dismiss.

To combine this with text field validation, use a text field delegate method or action method (configured in the text field's configuration handler when you create it) to enable/disable the UIAlertActions appropriately depending on what text has (or hasn't) been entered.

Here's an example. We created the text field like this:

alert.addTextFieldWithConfigurationHandler {
(tf:UITextField!) in
tf.addTarget(self, action: "textChanged:", forControlEvents: .EditingChanged)
}

We have a Cancel action and an OK action, and we brought the OK action into the world disabled:

(alert.actions[1] as UIAlertAction).enabled = false

Subsequently, the user can't tap OK unless there is some actual text in the text field:

func textChanged(sender:AnyObject) {
let tf = sender as UITextField
var resp : UIResponder = tf
while !(resp is UIAlertController) { resp = resp.nextResponder() }
let alert = resp as UIAlertController
(alert.actions[1] as UIAlertAction).enabled = (tf.text != "")
}

EDIT Here's the current (Swift 3.0.1 and later) version of the above code:

alert.addTextField { tf in
tf.addTarget(self, action: #selector(self.textChanged), for: .editingChanged)
}

and

alert.actions[1].isEnabled = false

and

@objc func textChanged(_ sender: Any) {
let tf = sender as! UITextField
var resp : UIResponder! = tf
while !(resp is UIAlertController) { resp = resp.next }
let alert = resp as! UIAlertController
alert.actions[1].isEnabled = (tf.text != "")
}

Prevent UIAlertController from dismissing on UIAlertAction

You can check the version of the app in appDelegate.

func applicationDidBecomeActive(_ application: UIApplication) {
//check the app version here and if version mismatch is there show the alert.
// if the version is different then make initial viewController as root view controller. and then present alert from that view controller.
}

Prevent Alert action from dismissing the UIAlertController if incorrect answer - Swift 4

You can't do this.
It's system behaviour - when you click on button alert closes and you can't prevent this.

Only one solution is to create a custom view controller that will look like native UIAlertController.

Dismissal of UIAlertController (best practice)

The dismissal is "included" in the presentViewController call. You do not need a delegate because you have the completion block. In this block you put what you would normally put into the delegate callback, except the call to dismiss the alert.

As far as "best practice" is concerned, I noted that in many APIs, Apple replaced delegate callbacks with completion blocks. Apple typically recommends using the block syntax. I surmise this could be partly because it helps keeping the related code sections together.



Related Topics



Leave a reply



Submit