Swift - How to present ViewController when tapping button in a custom AlertController - ios

I am developing in Swift 2.3
I have an Utils class enabling me to create UIAlertController easily.
public class Utils {
class func buildAlertInfo(withTitle title: String?, andMessage message: String?, withHandler handler: (UIAlertAction -> Void)?) -> UIAlertController {
let alertController = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
alertController.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: handler))
return alertController
}
}
It enables me to build AlertController with ease :
let alert = Utils.buildAlertInfo(withTitle: "Information", andMessage: "John Snow is dying", withHandler: nil)
self.presentViewController(alert, animated: false, completion: nil)
My issue now is that I want to create an other type of custom Alert in my Utils Class.
For example an Alert with a Button that navigates the user to a specific ViewController.
I don't know how can I access a ViewController in a Custom Class. Maybe pass as a parameter the ViewController I want to present after the Button is tapped ?
Should I respect the MVC pattern and not interact with the View in my Utils Class ?
EDIT :
The Alert I want should looks like this :
class func buildAlertInfoWithFavButton(withTitle title: String?, andMessage message: String?, withHandler handler: (UIAlertAction -> Void)?) -> UIAlertController {
let alertController = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
alertController.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: handler))
alertController.addAction(UIAlertAction(title: "Favorite", style: UIAlertActionStyle.Default, handler: handler))
return alertController
}
Where the OK action is the same, but the Favorite action should navigates you to the FavoriteViewController.

You can still use your buildAlertInfo function and can pass handler function like this way.
//Add function in your controller
func handler(action: UIAlertAction) {
//Add code of present
}
Now pass this function with your handler block
let alert = Utils.buildAlertInfo(withTitle: "Information",
andMessage: "John Snow is dying",
withHandler: self.handler)
**Edit:**For multiple action you can create array of handler with your method like this.
func buildAlertInfoWithFavButton(withTitle title: String?, andMessage message: String?, withHandler handler: [((UIAlertAction) -> Void)]?) -> UIAlertController {
alertController.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: handler.first))
alertController.addAction(UIAlertAction(title: "Favorite", style: UIAlertActionStyle.Default, handler: handler.last))
}
//Ok handler
func okHandler(action: UIAlertAction) {
//Add code of present
}
//Favorite handler
func favoriteHandler(action: UIAlertAction) {
//Add code of present
}
Now call the function like this.
let alert = Utils.buildAlertInfo(withTitle: "Information",
andMessage: "John Snow is dying",
withHandler: [okHandler, favoriteHandler])

What about
let alert = Utils.buildAlertInfo(
withTitle: "Information",
andMessage: "John Snow is dying",
withHandler: { action in self.go(to: specificViewController) }
)
self.presentViewController(alert, animated: false, completion: nil)
?

To be more Specific and use that method in any class of project. For this make a function in NSObject class. Like:
open class func showAlert(_ delegate: UIViewController, message: String ,strtitle: String, handler:((UIAlertAction) -> Void)! = nil)
{
let alert = UIAlertController(title: strtitle, message: message, preferredStyle: UIAlertControllerStyle.alert)
if handler == nil{
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
}
else
{
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: handler))
}
delegate.present(alert, animated: true, completion: nil)
}
In Controller I will call the method and do the required work like:
Alert.showAlert(self, message: "Message", strtitle: "Tittle!!", handler: {
(action : UIAlertAction) in
//Do your Work here
})
Note: Here Alert is name of NSObject class.

I would suggest using segue identifiers as the passed parameter (make sure you reference a segue that starts in the ViewController you call the "buildAlert" function from).
public class Utils {
class func buildAlertInfo(withTitle title: String?, andMessage message: String?, withSegue segueIdentifier: String?, sender: Any?) -> UIAlertController {
let alertController = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
alertController.addAction(UIAlertAction(title: "OK", style: .Default, handler: handler: {
action in
self.performSegue(withIdentifier: segueIdentifier, sender: sender)
})
return alertController
}
This can also be achieved without creating a new function, just sending the handler part from above as a parameter to the function you already have, like this:
let alert = Utils.buildAlertInfo(withTitle: "Information", andMessage: "John Snow is dying", withHandler: {
action in
self.performSegue(withIdentifier: "mySegueIdentifier", sender: self)
})
EDIT: Note that the sender part can be any object that has an #IBOutlet reference in the ViewController the function call takes place

Related

UIAlertController is not working in model class

I want to show an alert. But I want to show it by creating a function in another class and call that function from a viewcontroller. But it does not work.
Here is code from my LoginViewController:
class LoginViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
LoginModel().show_alert()
}
}
Here is code from my LoginModel:
class LoginModel{
let controller = LoginViewController()
public func show_alert(){
let alert = UIAlertController(title: "Title", message: "Some Message",
preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler:
nil))
controller.present(alert, animated: true, completion: nil)
}
}
You need to pass the reference of UIViewController subclass to your LoginModel class, to present the UIAlertViewController on LoginViewController. You should call show alert once the LoginViewController view is appeared on the screen, move the call to ViewWillApear or viewDidApear method
final class LoginViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidApear(animated)
LoginModel().show_alert(on: self)
}
}
final class LoginModel {
public func show_alert(on vc: UIViewController) {
let alert = UIAlertController(title: "Title", message: "Some Message",
preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler:
nil))
vc.present(alert, animated: true, completion: nil)
}
}
ideally you should not create the UI related methods in model classes, they should be on on UIViewController/UIView classes or their extension methods. Model classes should not know anything about UI stuff. So you can easily create simple extension method on UIViewController and call the showAlert method from viewController.
extension UIViewController {
func showAlert(_ title: String = "Alert", message: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler:
nil))
present(alert, animated: true, completion: nil)
}
}
you can call this method from UIViewController like
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
showAlert(message: "This is alert message")
}
Reminder: Your model shouldn't know about any UI related stuffs. Instead you should create an extension to UIViewController, or create free function
As a free function
func showAlertViewOnto(controller: UIViewController, detailInfo: (title: String?, message: String?), handler: ((UIAlertAction) -> Void)? = nil ) {
let alert = UIAlertController(title:detailInfo.title , message: detailInfo.message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler:
handler))
controller.present(alert, animated: true, completion: nil)
}
As an extension to your UIViewController
extension UIViewController {
func showAlertView(detailInfo: (title: String?, message: String?), handler: ((UIAlertAction) -> Void)? = nil ) {
let alert = UIAlertController(title:detailInfo.title , message: detailInfo.message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler:
handler))
self.present(alert, animated: true, completion: nil)
}
}
To Use it
showAlertViewOnto(controller: self, detailInfo: (title: "Hello ", message: "welcome to our service"), handler: { _ in
// here you can add code once ok is pressed
})
There is a logical error in your code. An instance of LoginViewController is already present (in the navigation stack or is the initial view controller) which appears on the screen.
You created a new instance of LoginViewController in your model class
let controller = LoginViewController()
which is not added to your navigation stack, so you don’t see it on the screen.
controller.present(alert, animated: true, completion: nil)
Presenting UIAlertController over the new instance will not show user an alert since the controller here itself is not present on the screen.
You will need to present UIAlertController from the instance visible on screen(the initial one). You could change the functionshow_alert to the following:
class LoginModel{
func showAlert(forController controller: UIViewController /*you could add title, message and other stuff here if needed.*/){
let alert = UIAlertController(title: "Title", message: "Some Message",
preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler:
nil))
controller.present(alert, animated: true, completion: nil)
}
And modify your call as follows:
LoginModel().showAlert(forController: self)

UIViewController and inheritance

I have multiple view controllers in my application. And in each of them I have to show alerts based on some conditions. Instead of adding alert controllers in each of them, I tried using inheritance as follows.
UIExtension.swift
class UIExtension: UIViewController {
func prepareAlert(title: String, message: String) -> UIAlertController {
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil))
return alert
}
}
FirstViewController.swift
class FirstViewController: UIExtension {
//somewhere inside used the following
present(prepareAlert(title: "Error Validation", message: "invalid fields"), animated: true, completion: nil)
}
Similarly, used UIExtension in other viewcontrollers to show alerts. Is this way recommended?
For something like this, you are better off adding your prepareAlert method to a UIViewController extension. No subclassing required.
extension UIViewController {
func prepareAlert(title: String, message: String) -> UIAlertController {
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil))
return alert
}
}
Then your view controller:
class FirstViewController: UIViewController {
//somewhere inside used the following
present(prepareAlert(title: "Error Validation", message: "invalid fields"), animated: true, completion: nil)
}
This allows you to use prepareAlert from any view controller include UITableViewController, UICollectionViewController, etc.
The approach is technically correct, although if you consider extending all UIViewController instances, regardless of any conditions, then it's more handy to extend it directly:
extension UIViewController {
func prepareAlert(title: String, message: String) -> UIAlertController {
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil))
return alert
}
}
rmaddy was faster. But I decided not to delete the answer, but add another idea.
Another approach is to use protocol as a wrapper for certain functionality, and this is also widely used.
Say, you have a protocol, associated with some functionality, like generating alert, in this case:
protocol Alertable {} // or whatever else name
extension Alertable {
func prepareAlert(title: String, message: String) -> UIAlertController {
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil))
return alert
}
}
Then, whenever you want certain UIViewController instance (or any other class, you get the idea) to be associated with this functionality, simply do:
class FirstViewController: UIViewController, Alertable {
// Now you can do the same:
present(prepareAlert(title: "Error Validation", message: "invalid fields"), animated: true, completion: nil)
}
To sum up, making up a protocol and extending it, and then associating certain classes with it - to expose that functionality - is a very handy and useful practice. In particular, this is a good way to encapsulate some functionality, for example, if you don't mean global/class-wide access to it.
Some extension method I am sharing with you that is used frequently in most application you can use it in any where of UIViewController class and enjoy :)
extension UIViewController {
let kAPPNAME = "Your App name"
func showOkAlert(_ msg: String) {
let alert = UIAlertController(title:
kAPPNAME, message: msg, preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alert.addAction(okAction)
present(alert, animated: true, completion: nil)
}
func showOkAlertWithHandler(_ msg: String,handler: #escaping ()->Void){
let alert = UIAlertController(title: kAPPNAME, message: msg, preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default) { (type) -> Void in
handler()
}
alert.addAction(okAction)
present(alert, animated: true, completion: nil)
}
func showAlertWithActions(_ msg: String,titles:[String], handler:#escaping (_ clickedIndex: Int) -> Void) {
let alert = UIAlertController(title: kAPPNAME, message: msg, preferredStyle: .alert)
for title in titles {
let action = UIAlertAction(title: title, style: .default, handler: { (alertAction) in
//Call back fall when user clicked
let index = titles.index(of: alertAction.title!)
if index != nil {
handler(index!+1)
}
else {
handler(0)
}
})
alert.addAction(action)
}
present(alert, animated: true, completion: nil)
}
func showOkCancelAlertWithAction(_ msg: String, handler:#escaping (_ isOkAction: Bool) -> Void) {
let alert = UIAlertController(title: kAPPNAME, message: msg, preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default) { (action) -> Void in
return handler(true)
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (action) -> Void in
return handler(false)
}
alert.addAction(cancelAction)
alert.addAction(okAction)
present(alert, animated: true, completion: nil)
}
}
USES
class FirstViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//Only Info
self.showOkAlert("Hello")
//Info with Okay button
self.showOkAlertWithHandler("Hello Again") {
print("Tap to Okay")
}
//Show alert with Okay and cancel
self.showOkCancelAlertWithAction("Hello with Cancel") { (isOk) in
if isOk {
print("Okay")
}
else {
print("Cancel")
}
}
//Show alert with actions
self.showAlertWithActions("Hello with action", titles: ["Allow","Don't Allow", "Cancel"]) { (tapIndex) in
if tapIndex == 1 {
print("Allow")
}
}
}
}

create alert function in all view controllers - swift

I'm trying to declare a function for showing alerts in my app. To avoid repeating work, i'm trying to use same function for all my app. I tried to do that by creating a class with function showNotification. but when i create an object of that class and call the method, nothing happens. How can i do that?
class SharedPropertiesAndMetods : UIViewController {
func showNotification(title: String, message: String)
{
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "تائید", style: .default, handler: nil)
alertController.addAction(defaultAction)
present(alertController, animated: true, completion: nil)
}
}
Use an extension like this
extension UIViewController {
func showAlert(title: String, message: String) {
let alertController = UIAlertController(title: title, message:
message, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: {action in
}))
self.present(alertController, animated: true, completion: nil)
}
}
call the function like this
self.showAlert(title: "hi", message: "test")
What I would do is to create a 'generic' view controller that do the job and than inherit from it:
1. If you want to display alert each time view did appear:
class GenericViewController: UIViewController {
// MARK: - View lifecycle -
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let notification = self.shouldDisplayAlertNotification() {
self.showNotification(notification)
}
}
// MARK: - Internal methods -
func shouldDisplayAlertNotification() -> AlertNotification? {
return nil
}
// MARK: - Private methods -
private func showNotification(_ alertNotification: AlertNotification) {
}
}
class MyController: GenericViewController {
override func shouldDisplayAlertNotification() -> AlertNotification? {
return AlertNotification(title: "Title", message: "Message")
}
}
Where AlertNotification is your custom model class:
class AlertNotification {
var title: String
var message: String
init(title: String, message: String) {
self.title = title
self.message = message
}
}
In this way, only VC that overrides shouldDisplayAlertNotificationwill display alert.
2. If you want to display alert on 'demand':
As suggested, extend UIViewController
extension UIViewController {
func showNotification(title: String, message: String) {
}
}
Actually you can declare a simple method anywhere outside class.
func showAlertWithCompletion(message:String,okTitle:String,cancelTitle:String?,completionBlock:#escaping (_ okPressed:Bool)->()){
let alertController = UIAlertController(title: AppName, message: message, preferredStyle: .alert)
let okAction = UIAlertAction(title: okTitle, style: .default) { (ok) in
completionBlock(true)
}
alertController.addAction(okAction)
if let cancelTitle = cancelTitle{
let cancelOption = UIAlertAction(title: cancelTitle, style: .cancel, handler: { (axn) in
completionBlock(false)
})
alertController.addAction(cancelOption)
}
if let topController = UIWindow.topViewController(){
topController.present(alertController, animated: true, completion: nil)
}
}
This way wherever you call it, you will get ok button pressed callback in completion handle or even make Extension as described by #Ganesh Kumar
Why not just an extension
extension UIViewController {
func showNotification(title: String, message: String)
{
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "تائید", style: .default, handler: nil)
alertController.addAction(defaultAction)
present(alertController, animated: true, completion: nil)
}
}
You can use this view controller extension to present alert view across the application.
https://github.com/SumitKr88/UIViewController-ShowAlertView/blob/master/UIViewController%2BExtensions.swift
extension UIViewController {
/// Show alert view
/// - Parameter title: title of alert
/// - Parameter message: message of alert
/// - Parameter actionTitles: List of action button titles(ex : "OK","Cancel" etc)
/// - Parameter style: Style of the buttons
/// - Parameter actions: actions repective to each actionTitles
/// - Parameter preferredActionIndex: Index of the button that need to be shown in bold. If nil is passed then it takes cancel as default button.
/**
Example usage:-
Just make sure actionTitles and actions array the same count.
/********** 1. Pass nil if you don't need any action handler closure. **************/
self.showAlert(title: "Title", message: "message", actionTitles: ["OK"], style: [.deafult], actions: [nil])
/*********** 2. Alert view with one action **************/
/// let okActionHandler: ((UIAlertAction) -> Void) = {(action) in
//Perform action of Ok here
}
self.showAlert(title: "Title", message: "message", actionTitles: ["OK", "CANCEL"], style: [.default, .cancel], actions: [okayActionHandler, nil])
/********** 3.Alert view with two actions **************/
let okActionHandler: ((UIAlertAction) -> Void) = {(action) in
//Perform action of ok here
}
let cancelActionHandler: ((UIAlertAction) -> Void) = {(action) in
//Perform action of cancel here
}
self.showAlert(title: "Title", message: "message", actionTitles: ["OK", "CANCEL"], style: [.default, .cancel], actions: [okActionHandler,cancelActionHandler], preferredActionIndex: 1)
*/
public func showAlert(title: String?,
message: String?,
actionTitles: [String?],
style: [UIAlertAction.Style],
actions: [((UIAlertAction) -> Void)?],
preferredActionIndex: Int? = nil) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
for (index, title) in actionTitles.enumerated() {
let action = UIAlertAction(title: title, style: style[index], handler: actions[index])
alert.addAction(action)
}
if let preferredActionIndex = preferredActionIndex { alert.preferredAction = alert.actions[preferredActionIndex] }
self.present(alert, animated: true, completion: nil)
}}
You could create extension to alertController and also have option for action handler. This will allow to use two different Alert controller based on handler is required or not.
extension UIAlertControler {
class func genericErrorAlert(forAlert message: String, completion: ((UIAlertAction) -> Void)? = nil )
let alert = UIAlertController(title: "Error", message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: completion))
return alert
}
}
You can also create one util file in your app, in that you can add any reusable method or function and use it anywhere in your app Like,
import Foundation
import UIKit
//MARK: - ALERT
func showMessage(title: String, message: String!, VC: UIViewController) {
let alert : UIAlertController = UIAlertController(title: "", message: message, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default) {
UIAlertAction in
}
alert.addAction(okAction)
VC.present(alert, animated: true, completion: nil)
}

swift3 call alert function from other swift.file

I am new of swift3. Now, I am finding a way to call alert function from other swift.file
Like this:
//MainView.swift
//Call function
AlertFun.ShowAlert(title: "Title", message: "message..." )
//Another page for storing functions
//Function.swift
public class AlertFun {
class func ShowAlert(title: String, message: String ) {
let alert = UIAlertController(title: tile, message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
Problem in here...Cannot do this in this way....
self.present(alert, animated: true, completion: nil)
How can I implement it? Thanks.
Pass the viewController reference as a parameter to the showAlert function like:
//MainView.swift
//Call function
AlertFun.ShowAlert(title: "Title", message: "message...", in: self)
//Another page for storing functions
//Function.swift
public class AlertFun {
class func ShowAlert(title: String, message: String, in vc: UIViewController) {
let alert = UIAlertController(title: tile, message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil))
vc.present(alert, animated: true, completion: nil)
}
}
Call Method for your controller
Utility.showAlertOnViewController(targetVC: self, title: "", message:"")
Your Class
class Utility: NSObject {
class func showAlertOnViewController(
targetVC: UIViewController,
title: String,
message: String)
{
let alert = UIAlertController(
title: title,
message: message,
preferredStyle: UIAlertControllerStyle.alert)
let okButton = UIAlertAction(
title:"OK",
style: UIAlertActionStyle.default,
handler:
{
(alert: UIAlertAction!) in
})
alert.addAction(okButton)
targetVC.present(alert, animated: true, completion: nil)
}
}
I found that none of the examples I've seen will work without getting the warning:
Attempt to present <UIAlertController: 0x7f82d8825400> on <app name> whose view is not in the window hierarchy!
The code that works for me is as follows. The function call is as before:
AlertFun.ShowAlert(title: "Title", message: "message...", in: self)
However, to make this work, the Function.swift file has to display the alert inside the DispatchQueue.main.async. So the Function.swift file should look like this:
public class AlertFun
{
class func ShowAlert(title: String, message: String, in vc: UIViewController)
{
DispatchQueue.main.async
{
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertAction.Style.default, handler: nil))
vc.present(alert, 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