UIAlert in Swift that automatically disappears? - ios

I have the following code:
/// Creates Alerts on screen for user.
func notifyUser(title: String, message: String) -> Void
{
let alert = UIAlertController(title: title,
message: message,
preferredStyle: UIAlertControllerStyle.Alert)
let cancelAction = UIAlertAction(title: "OK",
style: .Cancel, handler: nil)
alert.addAction(cancelAction)
UIApplication.sharedApplication().keyWindow?.rootViewController!.presentViewController(alert, animated: true,
completion: nil)
}
Which shows the following Alert:
I would prefer the Alert to appear for maybe 1-2 seconds and auto dismiss without having to click ok or dismiss. Is this possible?

Yes it's completely possible, I think the #Duncan C approach will work very well and it's self explanatory, so I going to explain you in code the #Duncan approach and another approach is using delays with the Grand Central Dispatch(GCD).
First Approach: Using the NSTimer class
// set the UIAlerController property
var alert: UIAlertController!
func notifyUser(title: String, message: String, timeToDissapear: Int) -> Void
{
alert = UIAlertController(title: title,
message: message,
preferredStyle: UIAlertControllerStyle.Alert)
let cancelAction = UIAlertAction(title: "OK",
style: .Cancel, handler: nil)
alert.addAction(cancelAction)
UIApplication.sharedApplication().keyWindow?.rootViewController!.presentViewController(alert, animated: true,
completion: nil)
// setting the NSTimer to close the alert after timeToDissapear seconds.
_ = NSTimer.scheduledTimerWithTimeInterval(Double(timeToDissapear), target: self, selector: Selector("dismissAlert"), userInfo: nil, repeats: false)
}
Second Approach: Using GCD
// set the UIAlerController property
var alert: UIAlertController!
func notifyUser(title: String, message: String, timeToDissapear: Int) -> Void
{
alert = UIAlertController(title: title,
message: message,
preferredStyle: UIAlertControllerStyle.Alert)
let cancelAction = UIAlertAction(title: "OK",
style: .Cancel, handler: nil)
alert.addAction(cancelAction)
UIApplication.sharedApplication().keyWindow?.rootViewController!.presentViewController(alert, animated: true,
completion: nil)
// Delay the dismissal by timeToDissapear seconds
let delay = Double(timeToDissapear) * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time, dispatch_get_main_queue()) { [weak self] in
self!.alert.dismissViewControllerAnimated(true, completion: nil)
}
}
And then you can call it in anywhere you want like in the following way :
self.notifyUser("Hello", message: "World", timeToDissapear: 3)
I hope this help you.

Here Is the code for Swift 4 Please Refer...Thank you
let alert = UIAlertController(title: "Success", message: "Record Updated Successfully", preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { action in
switch action.style{
case .default:
print("default")
case .cancel:
print("cancel")
case .destructive:
print("destructive")
}}))
self.present(alert, animated: true, completion: nil)
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
alert.dismiss(animated: true, completion: nil)
}

Sure. A UIAlertController is a special type of UIViewController. You're displaying it using presentViewController:animated:completion:. Just save a pointer to the UIAlertController into an instance variable, start a timer, and when the timer fires, call dismissViewControllerAnimated:completion:. You might want to get rid of the OK button action in that case, and if you leave the OK button you'll need to test and make sure your code works if you click OK before the timer fires.

Try this code:
var timer = NSTimer.scheduledTimerWithTimeInterval(5.0, target: self, selector: "dismissAlert", userInfo: nil, repeats: true)
func dismissAlert()
{
// Dismiss the alert from here
alert.dismissViewControllerAnimated(false, completion: nil)
}
I tried it and this worked fine. You can set the timer inside
scheduledTimerWithTimeInterval
where the current time is set to 5.0 seconds

func alert2 (_ dictKey: String){
if self.presentedViewController == nil {
let alertController = UIAlertController(title: nil, message: dictKey, preferredStyle: .alert )
alertController.addAction(UIAlertAction(title: "START AGAIN", style: UIAlertActionStyle.default, handler: {(action:UIAlertAction!) in self.returnToStart()}))
alertController.addAction(UIAlertAction(title: "REQUEST PIN", style: UIAlertActionStyle.default, handler:{(action:UIAlertAction!) in self.pinCreate()
self.dismiss(animated: false, completion: nil)//This dismisses the alert
}))
self.present(alertController, animated: true,completion: nil)
}
}
This proved to be a simple solution

func notifyUser(message: String) -> Void {
let alert = UIAlertController(title: "", message: message, preferredStyle: UIAlertController.Style.alert)
present(alert, animated: true, completion: nil)
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [unowned self] in
self.dismiss(animated: true)
}
}
To use:
notifyUser(message: "Image Saved")

Related

Swift UIKit - Showing an alert after another one

I am trying to show an alert after another one in swift. I found a solution but I think this is not the true way. My code piece is below.
With this code, alerts can be shown in the view.
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let person = people[indexPath.item]
let questionAc = UIAlertController(title: "What is your purpose?", message: "", preferredStyle: .alert)
let deleteButton = UIAlertAction(title: "Delete", style: UIAlertAction.Style.destructive) { [weak self] _ in
self?.people.remove(at: indexPath.item)
collectionView.reloadData()
}
let renameButton = UIAlertAction(title: "Rename", style: .default) { [weak self] _ in
let ac = UIAlertController(title: "Rename person", message: nil, preferredStyle: .alert)
ac.addTextField()
ac.addAction(UIAlertAction(title: "Cancel", style: .cancel))
ac.addAction(UIAlertAction(title: "OK", style: .default, handler: { [weak self, weak ac] _ in
guard let newName = ac?.textFields?[0].text else {return}
person.name = newName
self?.collectionView.reloadData()
}))
self?.present(ac, animated: true)
}
questionAc.addAction(deleteButton)
questionAc.addAction(renameButton)
present(questionAc, animated: true)
}
As you know, if I directly add my alert code after the first one, it doesn't work. But if I put my second one's code to first one's action, it does.
However, in this case, I need to connect one alert to another one's button's action. I don't want to connect it to another one.
Is there any solution for better way showing alerts in the same view, orderly?
Thank you.
Presenting the second alert works when done in the handler of one of the first alert's actions because the handler is executed after the alert is dismissed.
With this in mind, you just need to dismiss your first alert then present your second alert in the completion block of the dismissal call, for example:
(Here we just wait 2.5 seconds before presenting the second alert)
func showAlert1() {
let alert = UIAlertController(title: "Alert!", message: "I am alert number 1", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Action", style: .default, handler: { _ in
print("Alert 1 action pressed")
}))
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
present(alert, animated: true)
// Wait 2.5 seconds then dismiss the first alert and present the second alert
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(2500), execute: { [weak self] in
alert.dismiss(animated: true) {
self?.showAlert2()
}
})
}
If you want the first alert to remain open (so that it is still visible when the second alert is dismissed) you can just use the first alert as the presenting ViewController for example:
func showAlert1() {
let alert = UIAlertController(title: "Alert!", message: "I am alert number 1", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Action", style: .default, handler: { _ in
print("Alert 1 action pressed")
}))
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
// Wait 2.5 seconds then present the second alert
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(2500), execute: {
self.showAlert2(from: alert)
})
present(alert, animated: true)
}
func showAlert2(from viewController: UIViewController) {
let alert = UIAlertController(title: "Alert!", message: "I am alert number 2", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Action", style: .default, handler: { _ in
print("Alert 2 action pressed")
}))
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
viewController.present(alert, animated: true)
}

How to synchronize the UIAlert in viewDidAppear () [duplicate]

This question already has answers here:
UIAlertController visible only when viewDidAppear has finished its call
(2 answers)
Closed 4 years ago.
I want to display an alert screen using UIAlertController in viewDidAppear() and wait until the button is pressed (iOS 11).
When displaying UIAlertController by present, the alert screen is called in viewDidAppear(), but it is not displayed and the screen can not be tapped.
Alert screen by asynchronous or delayed execution. The alert screen is displayed without any problem.
Is there any good way to get in sync?
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
var doneloop = false
let alert = UIAlertController(title:"Title", message: "Message", preferredStyle: UIAlertControllerStyle.alert)
let action1 = UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {
(action: UIAlertAction!) in
print("push OK button")
doneloop = true // Runloop flag
})
alert.addAction(action1)
self.present(alert, animated: false, completion: nil)
while !doneloop {
RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.1))
}
alert.dismiss(animated: false, completion: nil)
}
I have created this method, just pass required data to this function with completion handling.....
//Alert with handling
func showMessageWithAction(_ messageText:String, messageTitle:String, okButtonTitle:String, cancelButtonTitle:String?, vc: UIViewController, completion:#escaping (Bool) -> Void) {
let attribute = NSMutableAttributedString.init(string: messageText)
attribute.addAttribute(NSAttributedStringKey.foregroundColor, value: UIColor.white , range: NSRange(location: 0,length: messageText.count))
let alert = UIAlertController(title: messageTitle,
message: messageText,
preferredStyle:
UIAlertControllerStyle.alert)
alert.view.tintColor = UIColor(red: (0/255.0), green: (29/255.0), blue: (97/255.0), alpha: 1.0)
alert.addAction(UIAlertAction(title: okButtonTitle,
style: UIAlertActionStyle.default,
handler: {(alert: UIAlertAction!) in
completion(true)
}))
if let cancelButton = cancelButtonTitle {
alert.addAction(UIAlertAction(title: cancelButton,
style: UIAlertActionStyle.cancel,
handler: {(alert: UIAlertAction!) in
completion(false)
}))
}
vc.present(alert, animated: true, completion: nil)
}
The way you do it is pretty messy, there is no need to synchronize it.
Also I would recommend you to run the alert presentation in viewWillAppear instead of viewDidAppear in case that you want see the alert after every entrency into this UIViewController.
In case that you would like to run it once, use viewDidLoad
I've created a wrapper around UIAlertController.
struct AlertWrapper {
static func presentAlert(for vc: UIViewController, with title: String, message: String, dismissAlertAction: UIAlertAction? = nil, applyAlertAction: UIAlertAction? = nil) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
if dismissAlertAction != nil {
alert.addAction(dismissAlertAction!)
}
if applyAlertAction != nil {
alert.addAction(applyAlertAction!)
}
vc.present(alert, animated: true, completion: nil)
}
}
Example inside methods above
let applyAction = UIAlertAction(title: "Ok", style: .default) { (_) in
// What ever you would like to do after pressing 'Ok'
}
let dismissAction = UIAlertAction(title: "Cancel", style: .default) { (_) in
// What ever you would like to do after pressing 'Cancel'
}
AlertWrapper.presentAlert(for: self, with: "YOUR TITTLE", message: "YOUR MESSAGE", dismissAlertAction: dismissAction, applyAlertAction: applyAction)
Fell free to ask any questions and next time please read rules here.
Example solution
Simple way
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let alert = UIAlertController(title:"Title", message: "Message", preferredStyle: UIAlertControllerStyle.alert)
let action1 = UIAlertAction(title: "OK", style: UIAlertActionStyle.default)
alert.addAction(action1)
self.present(alert, animated: false, completion: nil)
}

Swift: UIAlert in function - Use of unresolved identifier 'present'

I'm trying to limit the show of code so I just want to call function containing two strings to create a uialert faster with 1 line instead of 5/
The error I'm getting
Use of unresolved identifier 'present'
at the line
present(alert, animated: true, completion: nil)
// Controlling Alerts for Errors
func showAlert(titleString: String, messageString: String) {
// Alert to go to Settings
let alert = UIAlertController(title: titleString, message: messageString, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Dismiss", style: .default, handler: { _ in
alert.dismiss(animated: true, completion: nil)
}))
self.present(alert, animated: true, completion: nil)
}
In the comments, you explained that this is a stand-alone function. It should work if you make it an extension to UIViewController, for instance:
extension UIViewController {
public func showAlert(_ title:String, _ message:String) {
let alertVC = UIAlertController(
title: title,
message: message,
preferredStyle: .alert)
let okAction = UIAlertAction(
title: "OK",
style: .cancel,
handler: { action -> Void in
})
alertVC.addAction(okAction)
present(
alertVC,
animated: true,
completion: nil)
}
}
And to call it in a UIViewController:
showAlert(
"Could Not Send Email",
"Your device could not send e-mail. Please check e-mail configuration and try again."
)

Trying to present 2 UIAlertControllers back to back

I have a UIAlertController in which the options are populated from an array and are presented to the user. The user then selects an option from the alert. After this, I have a separate alert that provides the user with a confirmation message that has an okay button.
myAlert.addAction(UIAlertAction.init(title: item, style: .Default, handler: {
(UIAlertAction) in
self.chosenBusiness.append(businessNameData[item]!)
}))
self.presentViewController(myAlert, animated: true, completion: nil)
The code above gathers the data from the array and pushes it into actions in myAlert. The code above is inside of a for loop.
After this I use a function to retrieve the topmost view controller, and then push the next alert.
let top = topMostController()
let alertController = UIAlertController(title: "Location pinned", message: "You've successfully pinned this location, good work!", preferredStyle: UIAlertControllerStyle.Alert)
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default) {
(result : UIAlertAction) -> Void in
print("OK")
}
alertController.addAction(okAction)
self.presentViewController(myAlert, animated: true, completion: nil)
top.presentViewController(alertController, animated: true, completion: {
_ in
})
The error I receive is:
Attempting to load the view of a view controller while it is
deallocating and is not allowed and may result in undefined behavior.
UIAlertController: 0x1535b1cd0.
Can someone help me with this?
I think this is what you are looking for. The second must be called with the dismissal action of the first. Also, anytime you work with UI, It is safer to use dispatch_async(dispatch_get_main_queue()) {
\\code }
than not if you are not positive you are currently on the main queue.
let firstAlertController = UIAlertController(title: "First", message: "This is the first message.", preferredStyle: UIAlertControllerStyle.Alert)
let secondAlertController = UIAlertController(title: "Second", message: "This is the second message.", preferredStyle: UIAlertControllerStyle.Alert)
let secondDismissAction = UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default, completion: nil)
secondAlertController.addAction(secondDismissAction)
let firstDismissAction = UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default) {
UIAlertAction in
dispatch_async(dispatch_get_main_queue()) {
self.presentViewController(secondAlertController, animated: true, handler: nil)
}
}
firstAlertController.addAction(firstDismissAction)
self.presentViewController(firstAlertController, animated: true, completion: nil)

Swift Displaying Alerts best practices

I have various controllers in my app that all require validation, and when validation fails, I want to display an alert with the errors. Is there some best practice/design pattern for doing this? I could simply create a static function in a Helper class like so:
static func displayAlert(message: String, buttonTitle: String, vc: UIViewController)
{
let alertController = UIAlertController(title: "", message: message, preferredStyle: .Alert)
let okAction = UIAlertAction(title: buttonTitle, style: .Default, handler: nil)
alertController.addAction(okAction)
vc.presentViewController(alertController, animated: true, completion: nil)
}
But then I need to pass the view controller..which seems like bad practice. I could shoot off a notification and observe it, but that seems like overkill. Am I overthinking this, or is there some more acceptable way to go about handling something like this?
I ended up creating an extension for UIViewController and creating the alert function there:
extension UIViewController {
func alert(message: String, title: String = "") {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let OKAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController.addAction(OKAction)
self.present(alertController, animated: true, completion: nil)
}
}
Swift 4
I wanted this same functionality for myself, so I made a full extension. To use it, create a new swift file in your project and name it whatever you'd like. Place the following code inside:
import UIKit
extension UIViewController {
func presentAlertWithTitle(title: String, message: String, options: String..., completion: #escaping (Int) -> Void) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
for (index, option) in options.enumerated() {
alertController.addAction(UIAlertAction.init(title: option, style: .default, handler: { (action) in
completion(index)
}))
}
self.present(alertController, animated: true, completion: nil)
}
}
To use it (which so many people don't actually show, which can lead to confusion for a newbie like myself):
presentAlertWithTitle(title: "Test", message: "A message", options: "1", "2") { (option) in
print("option: \(option)")
switch(option) {
case 0:
print("option one")
break
case 1:
print("option two")
default:
break
}
}
As original answer from itstrueimryan at https://stackoverflow.com/a/30714429/6822183
Update for Swift 3:
extension UIViewController {
func alert(message: String, title: String = "") {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let OKAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController.addAction(OKAction)
self.present(alertController, animated: true, completion: nil)
}
}
I may have found a better answer to this problem, via an article by Krakendev: https://krakendev.io/blog/subclassing-can-suck-and-heres-why.
The idea is to use protocol-oriented programming to create a default implementation of an alert just for UIViewControllers:
protocol Alertable {
func issueAlert()
}
extension Alertable where Self: UIViewController {
func issueAlert() {
// alert code here
}
}
Now, just like that, every UIViewController that adheres to Alertable will have the issueAlert() method available to them without even having to define its own implementation.
And, of course, we can define parameters for the issueAlert function as well:
extension Alertable where Self: UIViewController {
func issueAlert(title: "Default Title", message: String = "Default Message") {
// alert code here
}
}
So our view controller can do either:
issueAlert()
or
issueAlert(title: "Error", message: "Something went wrong")
Two advantages to this approach that I can think of are that you know if a view controller has access to this method just by looking at the Alertable protocol in the class definition, and individual view controllers can override this method if they want to provide custom functionality. Of course, now you can also specify the Alertable contract as a method parameter.
Answer from Sigex is completely fine, except the int indices passing to trace the button clicks might not make sense because, caller needed to keep track with int value. In that case passing string arguments and comparing them in switch case in completion block makes more sense to me. I would rather use like,
import UIKit
extension UIViewController {
func presentAlertWithTitle(title: String, message: String, options: String..., completion: #escaping (String) -> Void) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
for (index, option) in options.enumerated() {
alertController.addAction(UIAlertAction.init(title: option, style: .default, handler: { (action) in
completion(options[index])
}))
}
self.present(alertController, animated: true, completion: nil)
}
}
And test with,
class TestViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
presentAlertWithTitle(title: "Test", message: "A sample message", options: "start", "stop", "cancel") { (option) in
print("option: \(option)")
switch(option) {
case "start":
print("start button pressed")
break
case "stop":
print("stop button pressed")
break
case "cancel":
print("cancel button pressed")
break
default:
break
}
}
}
}
Why not create a Utility function that returns the AlertView to the ViewController?
self.presentViewController(Utilities.createAlertController("errorMessage"), animated: true, completion: nil);
Updated for swift 3:
if you want to show the alert message to user used below simple lines of code;
// function defination:
func showMessageToUser(title: String, msg: String) {
let alert = UIAlertController(title: title, message: msg, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
//function call :
self.showMessageToUser(title: "Alert", msg: "your message to user")
// Enjoy coding..!
I used Sigex's extension in my code, however I have added a check, if options were used or not.
If no options are given in the call, then the Alert only shows "OK" and completes with returning option 0.
extension UIViewController {
func presentAlertWithTitle(title: String, message: String, options: String..., completion: #escaping (Int) -> Void) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
if options.count == 0 {
let OKAction = UIAlertAction(title: "OK", style: .default, handler: { (action) in
completion(0)
})
alertController.addAction(OKAction)
} else {
for (index, option) in options.enumerated() {
alertController.addAction(UIAlertAction.init(title: option, style: .default, handler: { (action) in
completion(index)
}))
}
}
self.present(alertController, animated: true, completion: nil)
}
}
Just omit the part , options: "1","2" then default alert is shown.
I love Sigex's extension, but I spiced it up a bit to add style on button depending on the title
func presentAlertWithOptions(title: String, message: String, options: String..., completion: #escaping (Int) -> Void) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
if options.count == 0 { //if there is no options, show a basic alert
let OKAction = UIAlertAction(title: "OK", style: .default, handler: { (action) in
completion(0)
})
alertController.addAction(OKAction)
} else { //alert with options
for (index, option) in options.enumerated() {
var alertStyle = UIAlertAction.Style.default
switch option { //check if we should style the buttons
case "Cancel": //cancel style
alertStyle = .cancel
case "Logout", "Discard Changes", "Discard", "Delete", "Remove": //destructive style
alertStyle = .destructive
default: break //keep as default
}
alertController.addAction(UIAlertAction(title: option, style: alertStyle, handler: { (action) in
completion(index)
}))
}
}
self.present(alertController, animated: true, completion: nil)
}
Swift 4.1
let alert = UIAlertController(title: "Atenção",message: "Mensagem Aqui",preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil))
self.present(alert, animated: true)

Resources