I have used side navigation menu(SWReveal). I have 4 ViewControllers. How can use same alertAction in different views.
You can create UIViewController extension like below:
extension UIViewController {
func showAlert(title: String?, message: String?, actionTitles:[String?], actions:[((UIAlertAction) -> Void)?]) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
for (index, title) in actionTitles.enumerated() {
let action = UIAlertAction(title: title, style: .default, handler: actions[index])
alert.addAction(action)
}
self.present(alert, animated: true, completion: nil)
}
}
And you can use this alert in UIViewController like below:
showAlert(title: "Your Title", message: "Your custom Message", actionTitles: ["Ok","Cancel"], actions: [{ action1 in
//OK Action
}, { action2 in
// Cancel Action
}
])
Hope will get your solution.
You can also use like this way.
class IOSPublicDefaultAlert: NSObject{
var viewController: UIViewController?
var actionCompletion: ((String) -> ())?
var alertTitle: String?
var alertMessage : String?
var alertType: UIAlertControllerStyle?
var actionTitleAndType: [String: UIAlertActionStyle]?
init(viewController : UIViewController,alertTitle: String?,alertMessage : String?,alertType: UIAlertControllerStyle = .alert,actionTitleAndType: [String: UIAlertActionStyle] ,actionCompletion : ((String)->())?){
super.init()
self.viewController = viewController
self.actionCompletion = actionCompletion
self.alertTitle = alertTitle
self.alertMessage = alertMessage
self.alertType = alertType
self.actionTitleAndType = actionTitleAndType
showAlert()
}
func showAlert(){
let alert = UIAlertController.init(title: alertTitle, message: alertMessage, preferredStyle: self.alertType ?? .alert)
for (actionTitle, actionType) in actionTitleAndType!{
let action = UIAlertAction(title: actionTitle, style: actionType) { (action) in
if let com = self.actionCompletion{
com(actionTitle)
}
}
alert.addAction(action)
}
viewController?.present(alert, animated: true, completion: nil)
}
}
and use add where you like as below sample
_ = IOSPublicDefaultAlert.init(viewController: self, alertTitle: "Warning!!!", alertMessage: alertMessage, actionTitleAndType: ["Ok" : .destructive, "Cancel" : .default], actionCompletion: { [unowned self] (title) in
if title == "Ok"{
}
})
In swift, your project, you can create a new .swift file and in this file create a class:
import UIKit
import Foundation
class yourFileName {
//Create a class function alerview
class func displayAlert(title: String, withMessage msg: String, andbtnTitle btntitle: String, in vc: UIViewController) {
let alert = UIAlertController(title: title, message: msg, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: btntitle, style: UIAlertActionStyle.default, handler: nil))
appDelegate.window?.rootViewController?.present(alert, animated: true, completion: nil)
}
}
//and now your any ViewController.swift file or any other file in your project you can access alert following way.
class viewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
yourfilename.displayAlert(title: "Alert", withMessage msg: "my alert view display", andbtnTitle btntitle: "Ok", in vc: self) // access your alertview
}
}
I hope it's work for you.
Create BaseController with a method that can show alert.
//Copyright © 2017 dip. All rights reserved.
import UIKit
class BaseController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
///This is common method to show alert with same action
func showAlert() {
let alert = UIAlertController(title: "Alert", message: "my msg on alert", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
///This will be common alert ok aciton for all child controllers.
print("Do some userful common work..")
}))
self.present(alert, animated: true, completion: nil)
}
}
Inherit Your 4 controllers from BaseController
// Copyright © 2017 dip. All rights reserved.
//
import UIKit
class ChildVC: BaseController {
override func viewDidLoad() {
super.viewDidLoad()
//call show alert when ever you wish
///This method will call showAlert() method on super class (BaseController)
self.showAlert()
}
}
Call self.showAlert() method from child when you want show alert with common action.
// MARK: - Alertable View
protocol AlertableView {
// Use handler if need catch cancel alert action
typealias CompletionHandler = (() -> Void)
func displayAlert(with title: String, message: String, actions: [UIAlertAction]?)
func displayAlert(with title: String, message: String, style: UIAlertControllerStyle, actions: [UIAlertAction]?, completion: CompletionHandler?)
}
extension AlertableView where Self: UIViewController {
func displayAlert(with title: String, message: String, actions: [UIAlertAction]?) {
self.displayAlert(with: title, message: message, style: .alert, actions: actions, completion: nil)
}
func displayAlert(with title: String, message: String, style: UIAlertControllerStyle, actions: [UIAlertAction]?, completion: CompletionHandler?) {
let alertCancelAction = UIAlertAction(title: "Cancel".localized, style: .cancel) { (action) in
guard let completion = completion else { return }
completion()
}
let alertController = UIAlertController(title: title, message: message, preferredStyle: style)
if let actions = actions {
for action in actions {
alertController.addAction(action)
}
alertController.addAction(alertCancelAction)
} else {
// If not any custom actions, we add OK alert button
let alertOkAction = UIAlertAction(title: "OK".localized, style: .cancel) { (action) in
guard let completion = completion else { return }
completion()
}
alertController.addAction(alertOkAction)
}
self.present(alertController, animated: true, completion: nil)
}
}
Create a common function ,
import UIKit
class AlertClass: NSObject {
func showAlertWithVC(_ VC : UIViewController, andMessage message: String ){
DispatchQueue.main.async {
let alert = UIAlertController(title: "APPLICATION_NAME", message: message , preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: nil))
VC.present(alert, animated: true, completion: nil)
}
}
}
Simply call AlertClass().showAlertWithVC() where you want to show Alert.
Related
Why I am getting Cannot find 'UIAlertController' in scope? error if i write alert function in separate swift file or if i use extension its not coming to another viewcontroller why?
code:
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)
}
error:
Cannot find 'UIAlertController' in scope,
Cannot find 'UIAlertAction' in scope
Cannot find 'self' in scope
Probably you forgot this in your viewController:
import UIKit
so your file has no idea what UIAlertController or UIAlertAction is
if you want to create an alert view controller to be used across multiple UIViewController you can do this:
import UIKit
class CustomAlertController: NSObject {
let message:String?
let title:String?
init(title:String, message:String) {
self.message = message
self.title = title
}
func showAlert()->UIAlertController {
let alertController = UIAlertController(title: self.title, message: self.message, preferredStyle: .alert)
// you can further customize your buttons, buttons' title etc
alertController.addAction(UIAlertAction(title: "OK", style: .default, handler: {action in
}))
return alertController
}
}
then your view controllers
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let alert = CustomAlertController(title: "Hello", message: "My message to the world")
DispatchQueue.main.async {
self.present(alert.showAlert(), animated: true, completion: nil)
}
}
}
Is there any separate global function to add a different style and a different handler for alerts?
My function from AppDelegate looks like this:
static func showAlertView(vc : UIViewController, titleString : String , messageString: String) ->()
{
let alertView = UIAlertController(title: titleString, message: messageString, preferredStyle: .alert)
let alertAction = UIAlertAction(title: "ok", style: .cancel) { (alert) in
vc.dismiss(animated: true, completion: nil)
}
alertView.addAction(alertAction)
vc.present(alertView, animated: true, completion: nil)
}
You just need to add more parameters to your function. In the code below I've added the following: controllerStyle for UIAlertController, actionStyle for UIAlertAction and action for UIAlertAction handler.
static func showAlertView(vc : UIViewController, titleString : String , messageString: String, controllerStyle: UIAlertController.Style = .alert, actionStyle: UIAlertAction.Style = .cancel, action: #escaping () -> () = {}) {
let alertView = UIAlertController(title: titleString, message: messageString, preferredStyle: controllerStyle)
let alertAction = UIAlertAction(title: "ok", style: .cancel) { (alert) in
if action == {} {
vc.dismiss(animated: true, completion: nil)
} else {
action()
}
}
alertView.addAction(alertAction)
vc.present(alertView, animated: true, completion: nil)
}
In UIKit, it is common to present UIAlertController for modal pop up alert messages in response to some action.
Is there a modal alert controller type in SwiftUI?
Is there a way to present a UIAlertController from SwiftUI classes? It seems like this may be possible using UIViewControllerRepresentable but not sure if that is required?
This just works:
class func alertMessage(title: String, message: String) {
let alertVC = UIAlertController(title: title, message: message, preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default) { (action: UIAlertAction) in
}
alertVC.addAction(okAction)
let viewController = UIApplication.shared.windows.first!.rootViewController!
viewController.present(alertVC, animated: true, completion: nil)
}
Put it in a Helper-Class.
Usage:
Helper.alertMessage(title: "Test-Title", message: "It works - even in SwiftUI")
Use Alert instead.
import SwiftUI
struct SwiftUIView: View {
#State private var showAlert = false;
var body: some View {
Button(action: { self.showAlert = true }) {
Text("Show alert")
}.alert(
isPresented: $showAlert,
content: { Alert(title: Text("Hello world")) }
)
}
}
Bind to isPresented in order to control the presentation.
I'm using extension of UIViewController to get current vc and UIAlertController to present 'Alert'. Maybe you can try this as followings:
extension for UIViewController
extension UIViewController {
class func getCurrentVC() -> UIViewController? {
var result: UIViewController?
var window = UIApplication.shared.windows.first { $0.isKeyWindow }
if window?.windowLevel != UIWindow.Level.normal {
let windows = UIApplication.shared.windows
for tmpWin in windows {
if tmpWin.windowLevel == UIWindow.Level.normal {
window = tmpWin
break
}
}
}
let fromView = window?.subviews[0]
if let nextRespnder = fromView?.next {
if nextRespnder.isKind(of: UIViewController.self) {
result = nextRespnder as? UIViewController
result?.navigationController?.pushViewController(result!, animated: false)
} else {
result = window?.rootViewController
}
}
return result
}
}
extension for UIAlertController
extension UIAlertController {
//Setting our Alert ViewController, presenting it.
func presentAlert() {
ViewController.getCurrentVC()?.present(self, animated: true, completion: nil)
}
func dismissAlert() {
ViewController.getCurrentVC()?.dismiss(animated: true, completion: nil)
}
}
And you can create your showAlert function now
func showMyAlert() {
let myAlert = UIAlertController(title: "Confirm order", message: "Are you sure to order two box of chocolate?", preferredStyle: .alert)
let okAction = UIAlertAction(title: "Ok!", style: .default) { (_) in
print("You just confirm your order")
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (_) in
print(You cancel it already!)
}
myAlert.addAction(okAction)
myAlert.addAction(cancelAction)
myAlert.presentAlert()
}
Wish my answer can help you. :-)
You can present UIKit Alert in SwiftUI using notificationCenter
At SceneDelegate.swift on "func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions)" insert this code {
NotificationCenter.default.addObserver(self, selector: #selector(self.showAlert), name: Notification.Name("showAlert"), object: nil)
and add this function
#objc private func showAlert(notification: NSNotification){
let msg: String = notification.object as! String
let alert = UIAlertController(title: "Title", message: msg, preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "οκ", style: .cancel) { (action) in
}
alert.addAction(cancelAction)
DispatchQueue.main.async {
self.window?.rootViewController?.present(alert, animated: true, completion: nil)
}
}
Now you can make the app to appear a message with UIKit AlertController writing this code on an action in a swifui class
var body: some View {
Button(action:{
NotificationCenter.default.post(name: Notification.Name("showAlert"), object: "Ελέγξτε το δίκτυο σας και προσπαθήστε αργότερα.")
}
}
I would like to create an action sheet that can be used several time in my code. To do so, I need to be able to use functions according to the action sheet title. Is there a way to pass functions as a parameter array like the "title" parameter?
//MARK: - UIAlert action sheet title
enum ActionSheetLabel: String {
case camera = "Camera"
case photoLibrary = "Album"
case cancel = "Cancel"
}
class CameraHandler {
static let cameraHandler = CameraHandler()
func openCamera() { }
func openPhotoLibrary() { }
}
//MARK: - Alert that shows an action sheet with cancel
extension UIViewController {
func showActionSheetWithCancel(vc: UIViewController, title: [ActionSheetLabel] /*Make a function parameter here to match title*/) {
let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
for value in title {
actionSheet.addAction(UIAlertAction(title: value.rawValue, style: .default, handler: {
(alert: UIAlertAction!) -> Void in
//Use the parameter function here to match title
}))
}
actionSheet.addAction(UIAlertAction(title: ActionSheetLabel.cancel.rawValue, style: .cancel, handler: nil))
vc.present(actionSheet, animated: true, completion: nil)
}
}
For UIAlert you just need to change preferredStyle .alert it and it's working for UIAlert And and below code just copy and paste it working for UIActionSheet.
extension UIViewController {
func popupAlert(title: String?, message: String?, actionTitles:[String?], actionStyle:[UIAlertAction.Style], actions:[((UIAlertAction) -> Void)?]) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .actionSheet)
for (index, title) in actionTitles.enumerated() {
let action = UIAlertAction(title: title, style: actionStyle[index], handler: actions[index])
alert.addAction(action)
}
self.present(alert, animated: true, completion: nil)
}
}
Check below code For Usage
self.popupAlert(title: "Alert"), message: “Error in Loading”, actionTitles: ["Okey", "Email"], actionStyle: [.default, .default], actions: [nil,{ action in
// I have set nil for first button click
// do your code for second button click
}])
if you have any query then please comment me. Thank You
I have find out the best way to add an action sheet with cancel and as much action as needed.
Create an UIViewController extension with type alias:
//MARK: - Alert that shows an action sheet with cancel
extension UIViewController {
typealias AlertAction = () -> ()
typealias AlertButtonAction = (ActionSheetLabel, AlertAction)
func showActionSheetWithCancel(titleAndAction: [AlertButtonAction]) {
let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
for value in titleAndAction {
actionSheet.addAction(UIAlertAction(title: value.0.rawValue, style: .default, handler: {
(alert: UIAlertAction!) -> Void in
value.1()
}))
}
actionSheet.addAction(UIAlertAction(title: ActionSheetLabel.cancel.rawValue, style: .cancel, handler: nil))
self.present(actionSheet, animated: true, completion: nil)
}
}
Then, in the class or other place where you want to use it, add the method this way:
//MARK: - UIAlert action sheet title
enum ActionSheetLabel: String {
case camera = "Camera"
case photoLibrary = "Album"
case cancel = "Cancel"
}
//MARK: - Class example where to use the action sheet action
class CameraHandler {
fileprivate let currentVC: UIViewController!
func openCamera() {
// Open user camera
}
func openPhotoLibrary() {
// Open user photo library
}
// Method example of this action sheet
func showActionSheetWithCameraAndLibrary(vc: UIViewController) {
//This is the way to use the extension
vc.showActionSheetWithCancel(titleAndAction: [
(ActionSheetLabel.camera, { [weak self] in self?.openCamera() }),
(ActionSheetLabel.photoLibrary, { [weak self] in self?.openPhotoLibrary() })
])
}
}
You can pass a closure and call it in the handler something like this should work.
Also not sure why you were passing the UIViewController , as you're already defining the function in a extension UIViewController therefore i allowed my self to remove it and used self.present instead .
extension UIViewController {
func showActionSheetWithCancel(title: [ActionSheetLabel], action: #escaping () -> ()?) {
let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
for value in title {
actionSheet.addAction(UIAlertAction(title: value.rawValue, style: .default, handler: {
(alert: UIAlertAction!) -> Void in
// action
action()
}))
}
let alertAction = UIAlertAction(title: ActionSheetLabel.cancel.rawValue, style: .cancel) { (_) in
action() // or for cancel call it here
}
actionSheet.addAction(alertAction)
self.present(actionSheet, animated: true, completion: nil)
}
}
As you can see #escaping () -> ()? is optional so you can pass nil too .
from what I understood you need to call a specific functions when the title of the alert changes & also you want to be able to do so from different viewControllers,
I hope this will help
extension UIViewController {
func showActionSheetWithCancel(vc: UIViewController, title: [ActionSheetLabel] ) {
let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
let cameraHandler = CameraHandler()
for value in title {
switch value.rawValue {
case ActionSheetLabel.camera.rawValue:
actionSheet.addAction(UIAlertAction(title: ActionSheetLabel.camera.rawValue, style: .default, handler: { (alert) in
cameraHandler.openCamera()
}))
case ActionSheetLabel.photoLibrary.rawValue:
actionSheet.addAction(UIAlertAction(title: ActionSheetLabel.photoLibrary.rawValue, style: .default, handler: { (alert) in
cameraHandler.openPhotoLibrary()
}))
default:
actionSheet.addAction(UIAlertAction(title: ActionSheetLabel.cancel.rawValue, style: .cancel, handler: nil))
}
vc.present(actionSheet, animated: true, completion: nil)
}
}
}
and the call of the function will be like this:
showActionSheetWithCancel(vc: self, title: [UIViewController.ActionSheetLabel.camera])
I have a main class, AddFriendsController, that runs the following line of code:
ErrorReporting.showMessage("Error", msg: "Could not add student to storage.")
I then have this ErrorReporting.swift file:
import Foundation
class ErrorReporting {
func showMessage(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.presentViewController(alert, animated: true, completion: nil)
}
}
Obviously, self wouldn't work here, and is giving me an error. How can I refer to the currently open view controller (i.e. AddFriendsController in this circumstance), as I am wishing to use this same method in many different swift files?
Thanks.
You can create extension method for UIApplication (for example) which will return your topViewController:
extension UIApplication {
static func topViewController(base: UIViewController? = UIApplication.sharedApplication().delegate?.window??.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return topViewController(nav.visibleViewController)
}
if let tab = base as? UITabBarController, selected = tab.selectedViewController {
return topViewController(selected)
}
if let presented = base?.presentedViewController {
return topViewController(presented)
}
return base
}
}
And then your class will look like this:
class ErrorReporting {
static func showMessage(title: String, msg: String) {
let alert = UIAlertController(title: title, message: msg, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil))
UIApplication.topViewController()?.presentViewController(alert, animated: true, completion: nil)
}
}
Method need to be static to be able to call it as ErrorReporting.showMessage.
Actually, in my opinion the view controller presenting operation should be done on the UIViewController instance, not in a model class.
A simple workaround for it is to pass the UIViewController instance as a parameter
class ErrorReporting {
func showMessage(title: String, msg: String, `on` controller: UIViewController) {
let alert = UIAlertController(title: title, message: msg, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil))
controller.presentViewController(alert, animated: true, completion: nil)
}
}
And call it like below
ErrorReporting.showMessage("Error", msg: "Could not add student to storage.", on: self)
Swift 3 version of Maksym Musiienko's answer would be the following:
extension UIApplication {
static func topViewController(base: UIViewController? = UIApplication.shared.delegate?.window??.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return topViewController(base: nav.visibleViewController)
}
if let tab = base as? UITabBarController, let selected = tab.selectedViewController {
return topViewController(base: selected)
}
if let presented = base?.presentedViewController {
return topViewController(base: presented)
}
return base
}
}