What is the best way to check if a UIAlertController is already presenting?
It is not the UIAlertController that is "already presenting", it is MessagesMasterVC. A view controller can only present one other view controller at a time. Hence the error message.
In other words, if you have told a view controller to presentViewController:...
, you cannot do that again until the presented view controller has been dismissed.
You can ask the MessagesMasterVC whether it is already presenting a view controller by examining its presentedViewController
. If not nil
, do not tell it to presentViewController:...
- it is already presenting a view controller.
Warning: UIAlertController is already presenting
You need to change your code like this, present the second alertController on completion of dismiss. Also change your if condition with my one.
if presentedViewController == nil {
self.presentViewController(alertController, animated: true, completion: nil)
} else{
self.dismissViewControllerAnimated(false) { () -> Void in
self.presentViewController(alertController, animated: true, completion: nil)
}
}
Hope this will help you.
Attempt to present UIAlertController on View Controller which is already presenting (null) [Swift]
The problem is really simple, you are trying to display another UIAlertController
on the currently presented UIAlertController
.
So, how to solve such a case?
You need to get a list of all
UIAlertController
's you use in your current view controller.You have to check the logic for displaying alerts in your current view controller (or other view controllers if you are doing async requests).
Your code must be like this when you want to display one alert on top of another.
Assume loadingAlert is currently displaying on the screen:
self.loadingAlert.dismiss(animated: true, completion: {
let anotherAlert = UIAlertController(title: "New One", message: "The Previous one is dismissed", preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
anotherAlert.addAction(okAction)
self.present(anotherAlert, animated: true, completion: nil)
})
You have to dismiss the first one before the next one can appear. I made this answer for dismissing an alert without buttons on it to make it more efficient.
So, what about the alert with action buttons?
It will dismiss automatically when you click one of the action
buttons onUIAlertController
that you created.
But, if you are displaying two UIAlertController
s which include UIButton
s at the same time, the problem will still occur. You need to re-check the logic for each, or you can handle it in the handler for each action :
self.connectionErrorAlert.dismiss(animated: true, completion: {
let anotherAlert = UIAlertController(title: "New One", message: "The Previous one is dismissed", preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default, handler: {action in
let nextAlert = UIAlertController(title: "New One", message: "The Previous one is dismissed", preferredStyle: .alert)
self.present(nextAlert, animated: true, completion: nil)
})
anotherAlert.addAction(okAction)
self.present(anotherAlert, animated: true, completion: nil)
})
For an Answer to Mike :
DispatchQueue.main.async(execute: {
if self.presentedViewController == nil {
print("Alert comes up with the intended ViewController")
var inputTextField = UITextField()
let textPrompt = UIAlertController(title: "Test", message: "Testing", preferredStyle: .alert)
textPrompt.addAction(UIAlertAction(title: "Continue", style: .default, handler: {
(action) -> Void in
// if the input matches the required text
let str = inputTextField.text
if str == requireTextInput {
print("right")
} else {
print("wrong")
}
}))
textPrompt.addTextField(configurationHandler: {(textField: UITextField!) in
textField.placeholder = ""
inputTextField = textField
})
weakSelf?.present(textPrompt, animated: true, completion: nil)
} else {
// either the Alert is already presented, or any other view controller
// is active (e.g. a PopOver)
// ...
let thePresentedVC : UIViewController? = self.presentedViewController as UIViewController?
if thePresentedVC != nil {
if let _ : UIAlertController = thePresentedVC as? UIAlertController {
print("Alert not necessary, already on the screen !")
} else {
print("Alert comes up via another presented VC, e.g. a PopOver")
}
}
}
})
Thanks to @Luke Answer : https://stackoverflow.com/a/30741496/3378606
UIAlertController Already presenting (null)
import UIKit
class ViewController: UIViewController {
let button = UIButton(type: .system)
override func viewDidLoad() {
super.viewDidLoad()
button.backgroundColor = .black
button.setTitle("Alert", for: .normal)
button.setTitleColor(.white, for: .normal)
button.addTarget(self, action: #selector(handleAlert), for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button)
button.heightAnchor.constraint(equalToConstant: 50).isActive = true
button.widthAnchor.constraint(equalToConstant: 100).isActive = true
button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
// Do any additional setup after loading the view.
}
@objc fileprivate func handleAlert() {
OperationQueue.main.addOperation {
showAlert(titulo: "YourTilte", mensagem: "YourMessage", vc: self)
print("On main thread: \(Thread.current.isMainThread)")
}
}
extension UIViewController {
func showAlert(titulo: String, mensagem: String, vc: UIViewController) {
let alerta = UIAlertController(title: titulo, message: mensagem, preferredStyle: .alert)
let acaoCancelar = UIAlertAction(title: "Ok", style: .cancel, handler: nil)
alerta.addAction(acaoCancelar)
vc.present(alerta, animated: true)
}
}
it work :)
Show UIAlertController if already showing an Alert
I found a workaround to find out which viewcontroller I can present the alert upon. I also posted the answer here:
@implementation UIViewController (visibleViewController)
- (UIViewController *)my_visibleViewController {
if ([self isKindOfClass:[UINavigationController class]]) {
// do not use method visibleViewController as the presentedViewController could beingDismissed
return [[(UINavigationController *)self topViewController] my_visibleViewController];
}
if ([self isKindOfClass:[UITabBarController class]]) {
return [[(UITabBarController *)self selectedViewController] my_visibleViewController];
}
if (self.presentedViewController == nil || self.presentedViewController.isBeingDismissed) {
return self;
}
return [self.presentedViewController my_visibleViewController];
}
@end
// To show a UIAlertController, present on the following viewcontroller:
UIViewController *visibleViewController = [[UIApplication sharedApplication].delegate.window.rootViewController my_visibleViewController];
Check if UIAlertController is Presented in an XCTest Case
"XCTest is not meant to be used to test UI components." is not truly accurate. I am using XCTest for almost every UI testing and it works just fine. The correct answer shall be "Mocking".
I would use OCMock for mocking the tested view controller and "verify" that the method presentViewController... is called with the alert controller. This is a neat solution and works just fine. (You can even ignore that the alert controller is passed to this method and just test that the view controller has been passed the method presentViewController...)
How to present UIAlertController when not in a view controller?
I posted a similar question a couple months ago and think I've finally solved the problem. Follow the link at the bottom of my post if you just want to see the code.
The solution is to use an additional UIWindow.
When you want to display your UIAlertController:
- Make your window the key and visible window (
window.makeKeyAndVisible()
) - Just use a plain UIViewController instance as the rootViewController of the new window. (
window.rootViewController = UIViewController()
) - Present your UIAlertController on your window's rootViewController
A couple things to note:
- Your UIWindow must be strongly referenced. If it's not strongly referenced it will never appear (because it is released). I recommend using a property, but I've also had success with an associated object.
- To ensure that the window appears above everything else (including system UIAlertControllers), I set the windowLevel. (
window.windowLevel = UIWindowLevelAlert + 1
)
Lastly, I have a completed implementation if you just want to look at that.
https://github.com/dbettermann/DBAlertController
Present UIAlertController in the currently active UIViewController
You can find the Top ViewController
on the navigation stack and directly present the AlertController
from there. You can use the extension method posted here to find the Top ViewController
from anywhere in your application:
https://stackoverflow.com/a/30858591/2754727
Presenting UIAlertController when UISearchController is Active
Can you try this to see if it works? Just replace the last line of your code where you present the action sheet.
if let presentedVC = presentedViewController {
presentedVC.present(emailAlert, animated: true, completion: nil)
} else {
present(emailAlert, animated: true, completion: nil)
}
Related Topics
Can a Standard Accessory View Be in a Different Position Within a Uitableviewcell
Ionic 2, Using Angular 2 Pipe Breaks on iOS-"Can't Find Variable: Intl"
Launch Images in iOS 7 with Xcode 5
Separation Between Header and First Cell -- in Plain Uitableview
Tableview:Numberofrowsinsection:]: Unrecognized Selector Sent to Instance
React Native iOS Build:Can't Find Node
How to Disable Calayer Implicit Animations
How to Get Screen Size Using Code on iOS
How to Change Color of Uitableviewcell When Selecting
How to Know If Nsassert Is Disabled in Release Builds
Sudzc Arc Version - Objc_Msgsend Call Causes Exc_Bad_Access Using 64-Bit Architecture
Ios: Perform Upload Task While App Is in Background
How to Pause and Resume Uiview.Animatewithduration
How to Cache Videos? iOS - Swift
How to Zoom a Uiscrollview Inside of a Uicollectionviewcell
Cross Directional Uiscrollviews - How to Modify the Scrolling Behaviour