I created a Utilities class to hold some common functions, one of which is an alertUser function that if called, will display an Alert box to the user with the provided title and message text. In another class file, I am validating some text field entries and if the validation doesn't pass, then I want to use the alertUser function from the Utilities class. However, when I do this, I get the following error message in the Xcode log:
Warning: Attempt to present <UIAlertController: 0x7f9c4be0b140> on <MyAppName.Utilities: 0x7f9c4be1cb60> whose view is not in the window hierarchy!
The calling code is in a UIViewController class file. Here's the code which is in the
class ItemSettingsVC: UIViewController:
private func validateNameField() -> Bool {
var passed = false
if (nameField.hasText) {
passed = true
} else {
Utilities().alertUser(strTitle: "Alert", strMessage: strInvalidNameFieldErrorMsg)
passed = false
}
return passed
}
Here's the alertUser function which is in the
class Utilities: UIViewController:
public func alertUser(strTitle: String, strMessage: String) {
let myAlert = UIAlertController(title: strTitle, message: strMessage, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil)
myAlert.addAction(okAction)
self.present(myAlert, animated: true, completion: nil)
}
This is running on iOS. I'm using Xcode 8 and swift 3. Any help is greatly appreciated. Thanks.
This should do it:
public func alertUser(strTitle: String, strMessage: String) {
let myAlert = UIAlertController(title: strTitle, message: strMessage, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil)
myAlert.addAction(okAction)
UIApplication.shared.delegate?.window??.rootViewController?.present(myAlert, animated: true, completion: nil)
}
You have to add an additional parameter in your alertUser function, which would be the VC that will present the alert controller.
for example:
public func alertUser(strTitle: String, strMessage: String, viewController: UIViewController) {
let myAlert = UIAlertController(title: strTitle, message: strMessage, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil)
myAlert.addAction(okAction)
viewController.present(myAlert, animated: true, completion: nil)
}
But I would recommend that you just make an extension of UIViewController and add your func alertUser()* there because you would surely use this alertUser in different VCs and complexity wise in my opinion, this would be more optimized.
Like this:
extension UIViewController {
func showAlert(title: String, message: String, callback: #escaping () -> ()) {
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: {
alertAction in
callback()
}))
self.present(alert, animated: true, completion: nil)
}
//add additional functions here if necessary
//like a function showing alert with cancel
}
NOTE : Please don't make your Utilities class a subclass of UIViewController, it would also be better to make it a struct handling static functions and/or variables
Use this class for easy to show Alert or ActionSheet
UIAlertController Extension
public extension UIAlertController {
public func showAlert(animated: Bool = true, completionHandler: (() -> Void)? = nil) {
guard let rootVC = UIApplication.shared.keyWindow?.rootViewController else {
return
}
var forefrontVC = rootVC
while let presentedVC = forefrontVC.presentedViewController {
forefrontVC = presentedVC
}
forefrontVC.present(self, animated: animated, completion: completionHandler)
}
}
AppAlert Class Create For UIAlertController Show
public class AppAlert {
private var alertController: UIAlertController
public init(title: String? = nil, message: String? = nil, preferredStyle: UIAlertControllerStyle) {
self.alertController = UIAlertController(title: title, message: message, preferredStyle: preferredStyle)
}
public func setTitle(_ title: String) -> Self {
alertController.title = title
return self
}
public func setMessage(_ message: String) -> Self {
alertController.message = message
return self
}
public func setPopoverPresentationProperties(sourceView: UIView? = nil, sourceRect:CGRect? = nil, barButtonItem: UIBarButtonItem? = nil, permittedArrowDirections: UIPopoverArrowDirection? = nil) -> Self {
if let poc = alertController.popoverPresentationController {
if let view = sourceView {
poc.sourceView = view
}
if let rect = sourceRect {
poc.sourceRect = rect
}
if let item = barButtonItem {
poc.barButtonItem = item
}
if let directions = permittedArrowDirections {
poc.permittedArrowDirections = directions
}
}
return self
}
public func addAction(title: String = "", style: UIAlertActionStyle = .default, handler: #escaping ((UIAlertAction!) -> Void) = { _ in }) -> Self {
alertController.addAction(UIAlertAction(title: title, style: style, handler: handler))
return self
}
public func addTextFieldHandler(_ handler: #escaping ((UITextField!) -> Void) = { _ in }) -> Self {
alertController.addTextField(configurationHandler: handler)
return self
}
public func build() -> UIAlertController {
return alertController
}
}
Used For Open AlertBox
AppAlert(title: "Question", message: "Are you sure?", preferredStyle: .alert)
.addAction(title: "NO", style: .cancel) { _ in
// action
}
.addAction(title: "Okay", style: .default) { _ in
// action
}
.build()
.showAlert(animated: true)
Used For ActionSheet Open
if UIDevice.current.userInterfaceIdiom != .pad {
// Sample to show on iPhone
AppAlert(title: "Question", message: "Are you sure?", preferredStyle: .actionSheet)
.addAction(title: "NO", style: .cancel) {_ in
print("No")
}
.addAction(title: "YES", style: .default) { _ in
print("Yes")
}
.build()
.showAlert(animated: true)
} else {
// Sample to show on iPad
AppAlert(title: "Question", message: "Are you sure?", preferredStyle: .actionSheet)
.addAction(title: "Not Sure", style: .default) {
_ in
print("No")
}
.addAction(title: "YES", style: .default) { _ in
print("Yes")
}
.setPopoverPresentationProperties(sourceView: self, sourceRect: CGRect.init(x: 0, y: 0, width: 100, height: 100), barButtonItem: nil, permittedArrowDirections: .any)
.build()
.showAlert(animated: true)
}
First find out the topmost viewController on your window .
Get the top ViewController in iOS Swift
and then present your alert on that viewController.No need to pass any parameter.
public func alertUser(strTitle: String, strMessage: String) {
let myAlert = UIAlertController(title: strTitle, message: strMessage, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil)
myAlert.addAction(okAction)
topmostVC().present(myAlert, animated: true, completion: nil)
}
Related
in the simple language: can we create that alert Box as a reusable method
i want to made 1 Alert box in to the function.
like this.
// this code has separate file
import UIKit
struct AlertView {
public func showAlertBox(title: String, message: String) -> UIAlertController {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: { _ in
}))
return alert
}
}
and here is my caller ViewController file code.
#IBAction func submitPressed(_ sender: Any) {
let alertView = AlertView()
let alert = alertView.showAlertBox(title: "Hours Added", message: "Hours have been updated")
alert.present(alert, animated: true) {
self?.dismiss(animated: true, completion: nil)
self?.timeSubmitted = true
self?.performSegue(withIdentifier: "unwindToMyHours", sender: nil)
}
}
You need alert action to performing ok action.
You can modify your code by this
Here are the helper functions.
struct AlertView {
public static func showAlertBox(title: String, message: String, handler: ((UIAlertAction)->Void)?) -> UIAlertController {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: handler))
return alert
}
}
extension UIAlertController {
func present(on viewController: UIViewController, completion: (() -> Void)? = nil) {
viewController.present(self, animated: true, completion: completion)
}
}
Usage
class ViewController: UIViewController {
#IBAction func submitPressed(_ sender: Any) {
AlertView.showAlertBox(title: "Hours Added", message: "Hours have been updated") { [weak self] action in
// Okay action code
}.present(on: self) { [weak self] in
self?.dismiss(animated: true, completion: nil)
self?.timeSubmitted = true
self?.performSegue(withIdentifier: "unwindToMyHours", sender: nil)
}
}
}
Note: self is dismissing so might be your alert is not presenting. You can present your alert on top most view controller. see this
Yes, you can create a shared alert controller. I would suggest making it a static method of your struct, or even a global function. It's silly to create an instance of your struct only to invoke a method that doesn't need any instance variables:
public static func alertBox(title: String, message: String) -> UIAlertController {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: { _ in
}))
return alert
}
}
And then you'd invoke it by saying
let alert = AlertView.alertBox(title: "title",message: "message" )
(Your function doesn't show the alert, it just creates it. I would therefore suggest naming it alertBox, not 'showAlertBox`.
Yes, you can use a shared alert controller. What I would suggest is making the AlertView struct, a singleton. You can change the struct as follows
struct AlertView {
// Shared instance
static let shared: AlertView = AlertView()
// Private initializer to prevent creating of new instances
private init() {}
public func showAlertBox(title: String, message: String) -> UIAlertController {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: { _ in
}))
return alert
}
}
By doing so, you will be able to create just one instance of AlertView and you have to use that single instance in your program. That way you won't have to create new instances of AlertView every time you need to display an alert. You can invoke it using,
let alert = AlertView.shared.showAlertBox(title: "Hours Added", message: "Hours have been updated")
Edit - You can refer this medium article to understand the singleton design patter
The best way is to perform simple encapsulation through extension, and complex encapsulation just loses applicability
example:
let alertVC = UIAlertController(title: "title", message: "message", preferredStyle: .alert)
.addActionTitles(titles) { (alertVC, action) in
let actionIdx = alertVC.actions.firstIndex(of: action)
DDLog(actionIdx)
}
self.present(alertVC, animated: true, completion:{})
code:
public let kTitleSure = "Yes"
public let kTitleCancell = "No"
/// contentViewController
public let kAlertContentViewController = "contentViewController"
#objc public extension UIAlertController{
///add UIAlertAction
#discardableResult
func addActionTitles(_ titles: [String]? = [kTitleCancell, kTitleSure], handler: ((UIAlertController, UIAlertAction) -> Void)? = nil) -> Self {
titles?.forEach({ (string) in
let style: UIAlertAction.Style = string == kTitleCancell ? .cancel : .default
self.addAction(UIAlertAction(title: string, style: style, handler: { (action) in
handler?(self, action)
}))
})
return self
}
///add textField
#discardableResult
func addTextFieldPlaceholders(_ placeholders: [String]?, handler: ((UITextField) -> Void)? = nil) -> Self {
if self.preferredStyle != .alert {
return self
}
placeholders?.forEach({ (string) in
self.addTextField { (textField: UITextField) in
textField.placeholder = string
handler?(textField)
}
})
return self
}
#discardableResult
func setContent(vc: UIViewController, height: CGFloat) -> Self {
setValue(vc, forKey: kAlertContentViewController)
vc.preferredContentSize.height = height
preferredContentSize.height = height
return self
}
}
github
In Swift, how can I return the string that I write into the UIAlertController's textfield, added if this UIAlertController is inside an extension
Usually if you put your UIAlertController implementation locally in your class, you can easily pass the text, but when the alert is into an extension, I'm not sure which could be the way to return the text.
For example, assume you have this extension:
extension UIViewController {
func presentTextFieldAlert(title: String, message: String, textFieldPlaceholder: String ) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let saveAction = UIAlertAction(title: "OK", style: UIAlertAction.Style.default) { _ -> Void in
let urlTextField = alertController.textFields![0] as UITextField
if urlTextField.text != nil { }
}
let cancelAction = UIAlertAction(title: "Cancel", style: .default)
alertController.addTextField { (textField: UITextField!) -> Void in
textField.placeholder = textFieldPlaceholder
}
alertController.addAction(saveAction)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
}
}
and into your class:
class Client: UIViewController {
func showAlert() {
self.presentTextFieldAlert(title: "Hello", message: "Write sth", textFieldPlaceholder: "Write here")
}
}
how can I pass the text from the alert to the viewcontroller?
I've tried something like:
class Client: UIViewController {
func showAlert() -> String {
return self.presentTextFieldAlert(title: "Hello", message: "Write sth", textFieldPlaceholder: "Write here")
}
}
but I think it is not the correct way.
Use a completion handler.
extension UIViewController {
func presentTextFieldAlert(title: String, message: String, textFieldPlaceholder: String, completion: #escaping (String?)->()) {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let saveAction = UIAlertAction(title: "OK", style: UIAlertAction.Style.default) { _ -> Void in
let urlTextField = alertController.textFields![0] as UITextField
completion(urlTextField.text)
}
let cancelAction = UIAlertAction(title: "Cancel", style: .default)
alertController.addTextField { (textField: UITextField!) -> Void in
textField.placeholder = textFieldPlaceholder
completion(nil)
}
alertController.addAction(saveAction)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
}
}
Then you can use it as:
class Client: UIViewController {
func showAlert() {
self.presentTextFieldAlert(title: "Hello", message: "Write sth", textFieldPlaceholder: "Write here") { (result) in
if let result = result {
// User entered some text and tapped OK.
}
}
}
}
This question already has answers here:
how to create an alert in a swift file model that works for various view controller
(2 answers)
Closed 4 years ago.
I am trying to create a alert box inside the swift file other than the UIViewController file. but I could not create it.
extension NetworkManager {
func showAlert(message: String,from:UIViewController, title: String = "") {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
let OKAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController.addAction(OKAction)
from.present(alertController, animated: true, completion: nil)
}
}
the above code is for implementing alertcontroller but I don't know how to pass the view controller I need to present so need assistance.
extension UIViewController {
func showAlert(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)
}
}
and use like this
from.showAlert(message:"Your message", title: "Title")
Add a Utilities class in your project.
class Utilities {
static func showSimpleAlert(OnViewController vc: UIViewController, Message message: String) {
//Create alertController object with specific message
let alertController = UIAlertController(title: "App Name", message: message, preferredStyle: .alert)
//Add OK button to alert and dismiss it on action
let alertAction = UIAlertAction(title: "OK", style: .default) { (action) in
alertController.dismiss(animated: true, completion: nil)
}
alertController.addAction(alertAction)
//Show alert to user
vc.present(alertController, animated: true, completion: nil)
}
}
Usage:
Utilities.showSimpleAlert(OnViewController: self, Message: "Some message")
Here is the extension I made. It allows to show either Alert or Action sheet and allows multiple actions "from the box"
extension UIViewController {
func presentAlert(title: String?, message: String, actions: UIAlertAction..., animated: Bool = true) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
actions.forEach { alert.addAction($0) }
self.present(alert, animated: animated, completion: nil)
}
func presentActionSheet(title: String?, message: String, actions: UIAlertAction..., animated: Bool = true) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .actionSheet)
actions.forEach { alert.addAction($0) }
self.present(alert, animated: animated, completion: nil)
}
}
Usage
let delete = UIAlertAction(title: "Delete", style: .destructive, handler: { _ in /* Your code here */})
let cancel = UIAlertAction(title: "Cancel", style: .default, handler: nil)
presentAlert(title: .albumPreferencesDeleteAlertTitle, message: "Very important message", actions: delete, cancel)
This is the more generalise method to show alert on view controller
func showAlert(msg: String, inViewController vc: UIViewController, actions: [UIAlertAction]? = nil, type: UIAlertControllerStyle = .alert, title: String = kAppName) {
let alertType: UIAlertControllerStyle = .alert
let alertTitle = kAppName
let alertVC = UIAlertController(title: alertTitle, message: msg, preferredStyle: alertType)
if let actions = actions {
for action in actions {
alertVC.addAction(action)
}
} else {
let actionCancel = UIAlertAction(title: "OK", style: .cancel, handler: nil)
alertVC.addAction(actionCancel)
}
vc.present(alertVC, animated: true, completion: nil)
}
Usage
AppUtilities.showAlert(msg: "Test msg", inViewController: self) //for alert
AppUtilities.showAlert(msg: "Test msg", inViewController: self, actions: [okAction, cancelAction]) //for alert
AppUtilities.showAlert(msg: "Test Msg", inViewController: self, type: .actionSheet) //shows action sheet
You can add this function in extension or create a separate utility class as you want.
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")
}
}
}
}
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)
}