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 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
.
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 != "")
}
Related Topics
Raw Image Data from Camera Like "645 Pro"
Creating Framework That Requires (Depends On) Another Framework
Nscache Doesn't Work with All Images When Loading for the First Time
How Does Xcode Find Implicit Target Dependencies
Mutating Self (Struct/Enum) Inside Escaping Closure in Swift 3.0
Convert Nsdata to Sockaddr Struct in Swift
How to Access Both Objective-C and Swift Classes from Same Storyboard
Button Action in Custom Uitableviewcell Affects Other Cells
How to Change Uidatepicker to a Specific Time (In Code)
Move a Node to Finger Using Swift + Spritekit
Write Extend File Attributes Swift Example
Wkwebview Content Loaded Function Never Get Called
Swift 2.0 Sorting Array of Objects by Property
Xcode 6 Keeps Renaming My App's Directory in iOS8 Simulator After Each Run
How to Get a Nsurl from an Xcassets Bundle