In my case, I need to show the user the UIAlertController upside down.
At the time of present, I can avoid the opening animation with:
present(alert, animated: false, completion: {
alert.view.transform = CGAffineTransform(rotationAngle: .pi)
})
At this point, the user sees the inverted UIAlertController.
And at the time of closing, after clicking on Cancel, UIAlertController blinks in the normal position for a hundredth of a second, which slightly spoils the UX.
With this simple example, you can catch this slight blink:
let alert = UIAlertController(title: "Title", message: "Message", preferredStyle: .alert)
let action = UIAlertAction(title: "Action", style: .default, handler: { action in })
let cancel = UIAlertAction(title: "Cancel", style: .destructive, handler: { action in })
alert.addAction(action)
alert.addAction(cancel)
present(alert, animated: false, completion: {
alert.view.transform = CGAffineTransform(rotationAngle: .pi)
})
I tried to call
alert.dismiss(animated: false, completion: nil)
or
alert.view.isHidden = true
inside cancel action, but it didn’t work.
Help me find a way to avoid this annoying end animation.
Just subclass UIAlertController like this:
class InstantCloseAlertController: UIAlertController {
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
UIView.setAnimationsEnabled(false)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
UIView.setAnimationsEnabled(true)
}
}
Related
I am preventing the user from going to the loginViewController from the mainMenuViewController, and it works good.
But if i go to another viewController, pop it to the mainMenu, and press fast the menuButton, it goes to the login instead entering the gestureRecognized func.
I have this code on the viewDidLoad()
let menuPressRecognizer = UITapGestureRecognizer()
menuPressRecognizer.addTarget(self, action: #selector(menuButtonAction(recognizer:)))
menuPressRecognizer.allowedPressTypes = [NSNumber(value: UIPress.PressType.menu.rawValue)]
self.view.addGestureRecognizer(menuPressRecognizer)
And this:
#objc func menuButtonAction(recognizer:UITapGestureRecognizer) {
let allertController=UIAlertController(title: R.string.strings.closeSession(), message: "", preferredStyle: .alert)
allertController.addAction(UIAlertAction(title: R.string.strings.closeSessionConfirm(), style: .default, handler: {_ in
UserDefaults.standard.set(false, forKey: .storedAutoLogin)
self.navigationController?.popViewController()
}))
allertController.addAction(UIAlertAction(title: R.string.strings.cancel(), style: .cancel, handler: nil))
self.present(allertController, animated: true, completion: nil)
}
Tried overriding pressesBegan, but it pop to login after printing "test", so i don't know why it don't override the pop
override func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
if(presses.first?.type == UIPress.PressType.menu) {
NSLog("test")
} else {
super.pressesBegan(presses, with: event)
}
}
#IBAction func tapDeleteButton(_ sender: UIButton) {
let alert = UIAlertController(title:"Are you sure?",message: "Do you really want to delete?",preferredStyle: UIAlertController.Style.alert)
let cancle = UIAlertAction(title: "Cancel", style: .default, handler: nil)
alert.addAction(cancle)
present(alert, animated: true, completion: nil)
let delete = UIAlertAction(title: "Delete", style: .destructive){(_) in
let uuidString = self.diary?.uuidString
NotificationCenter.default.post(name: NSNotification.Name("deleteDiary"), object: uuidString, userInfo: nil)
}
alert.addAction(delete)
present(alert, animated: true, completion: nil)
}
hello.
I'm studying swift while making a diary app.
But I'm stuck creating a delete button for a cell.
I get an error when I tap delete in alert.
How can I handle delete with notification and alert in there??
The problem is, you are trying to display another UIAlertController on the currently presented UIAlertController.
You are presenting your UIAlertController twice.
Remove the line present(alert, animated: true, completion: nil) under let alert = ....
The issue is because, You have presented the alert twice
Try this:
#IBAction func tapDeleteButton(_ sender: UIButton) {
let alert = UIAlertController(title:"Are you sure?",message: "Do you really want to delete?",preferredStyle: UIAlertController.Style.alert)
let cancle = UIAlertAction(title: "Cancel", style: .default, handler: nil)
let delete = UIAlertAction(title: "Delete", style: .destructive){(_) in
let uuidString = self.diary?.uuidString
NotificationCenter.default.post(name: NSNotification.Name("deleteDiary"), object: uuidString, userInfo: nil)
}
alert.addAction(cancle)
alert.addAction(delete)
present(alert, animated: true, completion: nil)
}
I'm creating an alert but I can't dismiss it when the user presses OK. I'm getting the following error:
2017-12-28 07:03:50.301947-0400 Prestamo[691:215874] API error:
<_UIKBCompatInputView: 0x10249adc0; frame = (0 0; 0 0); layer =
> returned 0 width, assuming
UIViewNoIntrinsicMetric
I was searching everywhere on the Internet but I couldn't find anything that helped me.
override func viewDidAppear(_ animated: Bool) {
createAlert(title: "Licencia2", message: "En el momento no tienes una licencia válida!")
}
func createAlert (title:String, message:String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {(action) in
alert.dismiss(animated: false, completion: nil)
}))
self.present(alert, animated: false, completion: nil)
}
Any ideas would be appreciated
You don't have to dismiss alert controller. It will automatically dismiss after the action handler has been called. Just remove the dismiss line.
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {(action) in
}))
self.present(alert, animated: false, completion: nil)
Please note the difference with this answer and you code.
You didn't invoked super.viewDidAppear(animated) and this causes problems.
Below code (this is all I used) works for me without problems (I've test it):
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
createAlert(title: "My test", message: "THis should work ok")
}
func createAlert (title:String, message:String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default) {
action in
NSLog("it is working ok");
})
present(alert, animated: true)
}
}
Opening the UIAlertController on button click, the action is going to open but main issue is the UIAlertAction methods are not performed on its click. Here is Code block :
class HomeViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// getData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//menuBtn is the button
#IBAction func menuBtn(sender: UIButton) {
let optionMenu = UIAlertController(title: nil, message: nil, preferredStyle: .ActionSheet)
let orders = UIAlertAction(title: "Orders", style: .Default, handler: { (alert: UIAlertAction!) -> Void in
let alertViewController = self.storyboard?.instantiateViewControllerWithIdentifier("OrdersViewController") as! OrdersViewController
self.presentViewController(alertViewController, animated: true, completion: nil)
})
let about = UIAlertAction(title: "About", style: .Default, handler: {(alert: UIAlertAction!) -> Void in
let aboutObject = self.storyboard?.instantiateViewControllerWithIdentifier("AboutViewController") as! AboutViewController
self.presentViewController(aboutObject, animated: true, completion: nil)
})
let contactUs = UIAlertAction(title: "Contact Us", style: .Default, handler: {(alert: UIAlertAction!) -> Void in
let alertViewController = self.storyboard?.instantiateViewControllerWithIdentifier("ContactViewController") as! ContactViewController
self.presentViewController(alertViewController, animated: true, completion: nil)
})
let login = UIAlertAction(title: "LogIn", style: .Default, handler: {(alert: UIAlertAction!) -> Void in
let alertViewController = self.storyboard?.instantiateViewControllerWithIdentifier("LoginViewController") as! LoginViewController
self.presentViewController(alertViewController, animated: true, completion: nil)
})
let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)
optionMenu.addAction(orders)
optionMenu.addAction(about)
optionMenu.addAction(contactUs)
optionMenu.addAction(login)
optionMenu.addAction(cancelAction)
self.presentViewController(optionMenu, animated: true, completion: nil)
}
This is code is working fine, I have checked it's opening new viewController as well.
Cross check points:
Controller class and stroybaord are connected
Storyboard ID has been assigned
IBAction must be connected to IBOutlet
On the Button click Action you have to write code.. Try This code.
let alert = UIAlertController(title: "Saved", message: "Selected Frame is Saved", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "Ok", style:.Default , handler: { (UIAlertAction) in
}))
//Add action like this
self.presentViewController(alert, animated: true, completion: nil)
Still need any help feel free to ask.
First of all check if the button action is touch up inside. Make the button action as touch up inside. Below code works for me. Hope this works for you as well. Change the action title according to your need.
#IBAction func menuBtn(sender: AnyObject) {
let actionSheet = UIAlertController()
let criticalAction = UIAlertAction(title : "CRITICAL" , style : UIAlertActionStyle.Default){
(action) in
//This section will be executed when the buttons are pressed
//Do your work here.
debugPrint("CRITICAL")
}
let highAction = UIAlertAction(title : "HIGH" , style : UIAlertActionStyle.Default){
(action) in
//This section will be executed when the buttons are pressed
//Do your work here.
debugPrint("HIGH")
}
actionSheet.addAction(criticalAction)
actionSheet.addAction(highAction)
self.presentViewController(actionSheet, animated: true, completion: nil)
}
I've been looking up a lot of tutorials on UIAlertController. Thus far, the way I found was to activate a UIAlertController by linking it to a button or label and then call a IBAction.
I tried to replicate the code to automatically pop an alert when user enters the app (I wanted to ask the user if they want to go through the tutorial). However, I keep getting the error:
Warning: Attempt to present UIAlertController on MainViewController whose view is not in the window hierarchy!
Then I tried to add the UIAlertController to the MainViewController via addChildViewController and addSubview. However, I get the error:
Application tried to present modally an active controller
I figured that I cannot use the presentViewController function and commented it out.
The UIAlertController is displayed BUT when I tried to click on the cancel or the never button, this error occurs.
Trying to dismiss UIAlertController with unknown presenter.
I am really stumped. Can someone share what I am doing wrong? Thank you so much. Here is the code.
func displayTutorial() {
alertController = UIAlertController(title: NSLocalizedString("tutorialAlert", comment: ""), message: NSLocalizedString("tutorialMsg", comment: ""), preferredStyle: .ActionSheet)
self.addChildViewController(alertController)
self.view.addSubview(alertController.view)
alertController.didMoveToParentViewController(self)
alertController.view.frame.origin.x = self.view.frame.midX
alertController.view.frame.origin.y = self.view.frame.midY
//alertController.popoverPresentationController?.sourceView = self.view*/
let OkAction = UIAlertAction(title: NSLocalizedString("yesh", comment: ""), style: .Destructive) { (action) in
}
alertController.addAction(OkAction)
let cancelAction = UIAlertAction(title: NSLocalizedString("notNow", comment: ""), style: .Destructive) { (action) in
//println(action)
self.tutorial = 1
self.presentedViewController?.dismissViewControllerAnimated(true, completion: nil)
}
alertController.addAction(cancelAction)
let neverAction = UIAlertAction(title: NSLocalizedString("never", comment: ""), style: .Cancel) { (action) in
self.tutorial = 1
}
alertController.addAction(neverAction)
//self.presentViewController(alertController, animated: false) {}
}
I found the solution. Apparently, I cannot call the UIAlertController from the func viewDidLoad. I must call the function from viewDidAppear. So my code now is
override func viewDidAppear(animated: Bool) {
if tutorial == 0 {
displayTutorial(self.view)
}
}
func displayTutorial(sender:AnyObject) {
let alertController = UIAlertController(title: NSLocalizedString("tutorialAlert", comment: ""), message: NSLocalizedString("tutorialMsg", comment: ""), preferredStyle: .ActionSheet)
let OkAction = UIAlertAction(title: NSLocalizedString("yesh", comment: ""), style: .Destructive) { (action) in
}
alertController.addAction(OkAction)
let cancelAction = UIAlertAction(title: NSLocalizedString("notNow", comment: ""), style: .Default) { (action) in
//println(action)
self.tutorial = 1
self.presentedViewController?.dismissViewControllerAnimated(true, completion: nil)
}
alertController.addAction(cancelAction)
let neverAction = UIAlertAction(title: NSLocalizedString("never", comment: ""), style: .Cancel) { (action) in
self.tutorial = 1
}
alertController.addAction(neverAction)
self.presentViewController(alertController, animated: true, completion: nil)
if let pop = alertController.popoverPresentationController {
let v = sender as UIView
pop.sourceView = view
pop.sourceRect = v.bounds
}
}
Thanks to this posting: Warning: Attempt to present * on * whose view is not in the window hierarchy - swift
Below UIAlertController with extension would help you show alert with dynamic number of buttons with completion handler for selected index
extension UIViewController {
func displayAlertWith(message:String) {
displayAlertWith(message: message, buttons: ["Dismiss"]) { (index) in
}
}
func displayAlertWith(message:String, buttons:[String], completion:((_ index:Int) -> Void)!) -> Void {
displayAlertWithTitleFromVC(vc: self, title: Bundle.main.infoDictionary!["CFBundleDisplayName"] as! String, andMessage: message, buttons: buttons, completion: completion)
}
func displayAlertWithTitleFromVC(vc:UIViewController, title:String, andMessage message:String, buttons:[String], completion:((_ index:Int) -> Void)!) -> Void {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
for index in 0..<buttons.count {
let action = UIAlertAction(title: buttons[index], style: .default, handler: {
(alert: UIAlertAction!) in
if(completion != nil){
completion(index)
}
})
alertController.addAction(action)
}
DispatchQueue.main.async {
vc.present(alertController, animated: true, completion: nil)
}
}
}
If you need to auto dismiss the alert you can call dismiss on presented view controller after some delay.
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) {
vc.dismiss(animated: true, completion: nil)
}
Hope this might help you.