Prevent UIAlertController to dismiss
I would like to prevent the UIAlertController
from dismissing.
I have a UIAlertAction
that simply appends a string into the UIAlertTextField, however, once tapped it dismisses the view controller [undesired]. I've tried adding an NSNotification with undesired results.
UIAlertAction *pasteMessage = [UIAlertAction actionWithTitle:@"Paste Message" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
UITextField *textField = alertC.textFields.firstObject;
textField.text = [textField.text stringByAppendingString:[NSString stringWithFormat:@"%@", copiedString]];
}];
I've also tried setting no to pasteMessage by:
[alertC canPerformAction:@selector(dismissViewControllerAnimated:completion:) withSender:pasteMessage];
-(void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion {
UIAlertController *alertController = (UIAlertController *)self.presentedViewController;
UIAlertAction *paste = alertController.actions.firstObject;
if (paste) {
flag = NO;
} else {
flag = YES;
}
}
Edit, i'm not looking to prevent the tapping of UIAlertAction
i'm looking to prevent the UIAlertController
from dismissing when tapping on said action. The action can be enabled/disabled whatever, but my goal is to simply paste the copied message into the UITextField
by pressing an action (hence the reason I don't want it to be dismissed)
I also realize setting the BOOL to dismissViewControllerAnimated:
simply sets it to not animate the view controllers dismissal, I don't want it to imply it was for stopping the actual dismissal process. Simply offering the things I've tried in relation to my goal. I've also tried presenting a new UIAlertController
when selecting pasteMessage auto-populating the new UIAlertControllers
textField with the copied message, it works, but I feel like it's too hacky for what could be done.
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