How do I present a UIAlertController in SwiftUI? - swift

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: "Ελέγξτε το δίκτυο σας και προσπαθήστε αργότερα.")
}
}

Related

SwiftUI: UIAlertController ActionSheet is full screen

I am trying to implement a check marked action sheet in a SwiftUI View. I am using a
UIViewControllerRepresentable to create a UIAlertController
struct WhatsAppAlertController: UIViewControllerRepresentable {
let viewModel: PropViewModel
func makeUIViewController(context: Context) -> UIAlertController {
let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
let contactsNumbers = viewModel.contactsNumbers()
for number in contactsNumbers {
let action = UIAlertAction(
title: "\(number.value.stringValue)",
style: .default,
handler: { _ in
self.viewModel.openWhatsAppURL(withNumber: number.value.stringValue)
})
alert.addAction(action)
}
let cancel = UIAlertAction(title: L10n.cancel, style: .cancel, handler: nil)
alert.addAction(cancel)
return alert
}
func updateUIViewController(_ uiViewController: UIAlertController, context: Context) {
}
}
It is displayed using
.sheet(isPresented: $showWhatsAppActionSheet) {
WhatsAppAlertController(viewModel: self.viewModel)
}
I have a feeling it is because the UIAlertController is being presented using .sheet
My plan was to use action.setValue(true, forKey: "checked") to checkmark and remember the selected option.
Is there a way to fix this? Or perhaps implement the checkmark using only SwiftUI?
My mistake was not creating a holder controller, a UIViewController to house the UIAlertController. The presentation should also be done with .background() rather than .sheet()
Here is the updated code:
struct WhatsappAlertController: UIViewControllerRepresentable {
#Binding var show: Bool
let viewModel: PropViewModel
func makeUIViewController(context: UIViewControllerRepresentableContext<WhatsappAlertController>) -> UIViewController {
return UIViewController() // holder controller - required to present alert
}
func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<WhatsappAlertController>) {
if self.show {
let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
let contactsNumbers = viewModel.contactsNumbers()
for number in contactsNumbers {
let action = UIAlertAction(
title: "\(number.value.stringValue)",
style: .default,
handler: { _ in
self.viewModel.openWhatsAppURL(withNumber: number.value.stringValue)
})
// action.setValue(true, forKey: "checked")
alert.addAction(action)
}
let cancel = UIAlertAction(title: L10n.cancel, style: .cancel, handler: nil)
alert.addAction(cancel)
DispatchQueue.main.async { // must be async !!
uiViewController.present(alert, animated: true, completion: {
self.show = false // hide holder after alert dismiss
})
}
}
}
}
And to display:
.background(WhatsappAlertController(show: self.$showWhatsAppActionSheet, viewModel: viewModel))

How can i make this app recognize multiple images

I have made an app that recognize's a piece of art/drawing/paper etc using ARKit. When it recognize's the images it switches to another View Controller and tells you about it. It can recognize one piece of art. The question is how can i have the app recognize multiple drawings. Here is the code.
AR View Controller:
import UIKit
import SpriteKit
import ARKit
import AVFoundation
struct ImageInformation {
let name: String
let description: String
let image: UIImage
}
class ViewController: UIViewController, ARSKViewDelegate {
#IBOutlet var sceneView: ARSKView!
#IBAction func options(){
let alert = UIAlertController(title: "Options", message: "Select one of the options below to continue.", preferredStyle: .actionSheet)
alert.addAction(UIAlertAction(title: "Enable Flashlight", style: .default, handler: { action in
//Enable Flashlight function
func toggleTorch(on: Bool) {
guard let device = AVCaptureDevice.default(for: AVMediaType.video),
device.hasTorch
else { return }
do {
try device.lockForConfiguration()
device.torchMode = on ? .on : .off
device.unlockForConfiguration()
} catch {
//Torch can not be used.
let alert = UIAlertController(title: "Flashlight Error", message: "We are unable to activate the flashlight. This could be because the flashlight is being used by another app", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Dissmis", style: .default, handler: nil))
alert.addAction(UIAlertAction(title: "Restart App", style: .destructive, handler: { action in
fatalError("The flashlight could not be used and user restarted app.")
}))
self.present(alert, animated: true)
}
}
toggleTorch(on: true)
}))
//Continue working on this function when you get back!!!
alert.addAction(UIAlertAction(title: "Disable Flashlight", style: .default, handler: { action in
//Enable flashlight function here
func toggleTorch(off: Bool) {
guard let device = AVCaptureDevice.default(for: AVMediaType.video),
device.hasTorch
else { return }
do {
try device.lockForConfiguration()
device.torchMode = off ? .on : .off
device.unlockForConfiguration()
} catch {
//Torch can not be used.
let alert = UIAlertController(title: "Flashlight Error", message: "We are unable to activate the flashlight. This could be because the flashlight is being used by another app", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Dissmis", style: .default, handler: nil))
alert.addAction(UIAlertAction(title: "Restart App", style: .destructive, handler: { action in
fatalError("The flashlight could not be used and user restarted app.")
}))
self.present(alert, animated: true)
}
}
toggleTorch(off: false)
}))
alert.addAction(UIAlertAction(title: "Clear Recently Visited Artworks", style: .destructive, handler: { action in
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let secondViewController = storyboard.instantiateViewController(withIdentifier: "action_done")
self.present(secondViewController, animated: false, completion: nil)
}))
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
self.present(alert, animated: true)
}
var selectedImage : ImageInformation?
let images = ["flower" : ImageInformation(name: "Flower Drawing", description: "This is a drawing of a flower and was made by the developer of this app. It was intended to be a thank you card for a teacher on Teacher appreciation day. The Teacher enjoyed the project.", image: UIImage(named: "flower")!)]
override func viewDidLoad() {
super.viewDidLoad()
sceneView.delegate = self
sceneView.showsFPS = false
sceneView.showsNodeCount = false
if let scene = SKScene(fileNamed: "Scene") {
sceneView.presentScene(scene)
}
guard let referenceImages = ARReferenceImage.referenceImages(inGroupNamed: "AR Resources", bundle: nil) else {
let alert = UIAlertController(title: "Resources not Found", message: "The files needed for this application to work propely can not be found. What would you like to do about this?", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Continue Anyway", style: .default, handler: nil))
alert.addAction(UIAlertAction(title: "Restart App", style: .default, handler: { action in
fatalError("The Recources could not be found on the users device.")
}))
self.present(alert, animated: true)
return
}
let configuration = ARWorldTrackingConfiguration()
configuration.detectionImages = referenceImages
sceneView.session.run(configuration, options: [.resetTracking, .removeExistingAnchors])
}
// MARK: - ARSKViewDelegate
func view(_ view: ARSKView, nodeFor anchor: ARAnchor) -> SKNode? {
if let imageAnchor = anchor as? ARImageAnchor,
let referenceImageName = imageAnchor.referenceImage.name,
let scannedImage = self.images[referenceImageName] {
self.selectedImage = scannedImage
self.performSegue(withIdentifier: "switch", sender: self)
return imageSeenMarker()
}
return nil
}
private func imageSeenMarker() -> SKLabelNode {
let labelNode = SKLabelNode(text: "✅")
labelNode.horizontalAlignmentMode = .center
labelNode.verticalAlignmentMode = .center
return labelNode
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "switch"{
if let imageInformationVC = segue.destination as? ImageInformationViewController,
let actualSelectedImage = selectedImage {
imageInformationVC.imageInformation = actualSelectedImage
}
}
}
}
Information View Controller:
import Foundation
import UIKit
class ImageInformationViewController : UIViewController {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var descriptionText: UILabel!
var imageInformation : ImageInformation?
override func viewDidLoad() {
super.viewDidLoad()
if let actualImageInformation = imageInformation {
self.nameLabel.text = actualImageInformation.name
self.imageView.image = actualImageInformation.image
self.descriptionText.text = actualImageInformation.description
}
}
#IBAction func dismissView(_ sender: Any) {
self.dismiss(animated: true, completion: nil)
}
}
How Can i do this?
Thanks!
The function referenceImages(inGroup:bundle:) loads all reference images from your assets. Just put multiple images into your AR Resource Group in your Assets and assign a unique name for all of them.

use same UIAlertController in different ViewControllers

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.

How to show an alert from another class in Swift?

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
}
}

How do we create and dismiss an UIAlertController without user input? (Swift)

I've been looking up a lot of tutorials on UIAlertController. Thus far, the way I found was to activate a UIAlertController by linking it to a button or label and then call a IBAction.
I tried to replicate the code to automatically pop an alert when user enters the app (I wanted to ask the user if they want to go through the tutorial). However, I keep getting the error:
Warning: Attempt to present UIAlertController on MainViewController whose view is not in the window hierarchy!
Then I tried to add the UIAlertController to the MainViewController via addChildViewController and addSubview. However, I get the error:
Application tried to present modally an active controller
I figured that I cannot use the presentViewController function and commented it out.
The UIAlertController is displayed BUT when I tried to click on the cancel or the never button, this error occurs.
Trying to dismiss UIAlertController with unknown presenter.
I am really stumped. Can someone share what I am doing wrong? Thank you so much. Here is the code.
func displayTutorial() {
alertController = UIAlertController(title: NSLocalizedString("tutorialAlert", comment: ""), message: NSLocalizedString("tutorialMsg", comment: ""), preferredStyle: .ActionSheet)
self.addChildViewController(alertController)
self.view.addSubview(alertController.view)
alertController.didMoveToParentViewController(self)
alertController.view.frame.origin.x = self.view.frame.midX
alertController.view.frame.origin.y = self.view.frame.midY
//alertController.popoverPresentationController?.sourceView = self.view*/
let OkAction = UIAlertAction(title: NSLocalizedString("yesh", comment: ""), style: .Destructive) { (action) in
}
alertController.addAction(OkAction)
let cancelAction = UIAlertAction(title: NSLocalizedString("notNow", comment: ""), style: .Destructive) { (action) in
//println(action)
self.tutorial = 1
self.presentedViewController?.dismissViewControllerAnimated(true, completion: nil)
}
alertController.addAction(cancelAction)
let neverAction = UIAlertAction(title: NSLocalizedString("never", comment: ""), style: .Cancel) { (action) in
self.tutorial = 1
}
alertController.addAction(neverAction)
//self.presentViewController(alertController, animated: false) {}
}
I found the solution. Apparently, I cannot call the UIAlertController from the func viewDidLoad. I must call the function from viewDidAppear. So my code now is
override func viewDidAppear(animated: Bool) {
if tutorial == 0 {
displayTutorial(self.view)
}
}
func displayTutorial(sender:AnyObject) {
let alertController = UIAlertController(title: NSLocalizedString("tutorialAlert", comment: ""), message: NSLocalizedString("tutorialMsg", comment: ""), preferredStyle: .ActionSheet)
let OkAction = UIAlertAction(title: NSLocalizedString("yesh", comment: ""), style: .Destructive) { (action) in
}
alertController.addAction(OkAction)
let cancelAction = UIAlertAction(title: NSLocalizedString("notNow", comment: ""), style: .Default) { (action) in
//println(action)
self.tutorial = 1
self.presentedViewController?.dismissViewControllerAnimated(true, completion: nil)
}
alertController.addAction(cancelAction)
let neverAction = UIAlertAction(title: NSLocalizedString("never", comment: ""), style: .Cancel) { (action) in
self.tutorial = 1
}
alertController.addAction(neverAction)
self.presentViewController(alertController, animated: true, completion: nil)
if let pop = alertController.popoverPresentationController {
let v = sender as UIView
pop.sourceView = view
pop.sourceRect = v.bounds
}
}
Thanks to this posting: Warning: Attempt to present * on * whose view is not in the window hierarchy - swift
Below UIAlertController with extension would help you show alert with dynamic number of buttons with completion handler for selected index
extension UIViewController {
func displayAlertWith(message:String) {
displayAlertWith(message: message, buttons: ["Dismiss"]) { (index) in
}
}
func displayAlertWith(message:String, buttons:[String], completion:((_ index:Int) -> Void)!) -> Void {
displayAlertWithTitleFromVC(vc: self, title: Bundle.main.infoDictionary!["CFBundleDisplayName"] as! String, andMessage: message, buttons: buttons, completion: completion)
}
func displayAlertWithTitleFromVC(vc:UIViewController, title:String, andMessage message:String, buttons:[String], completion:((_ index:Int) -> Void)!) -> Void {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
for index in 0..<buttons.count {
let action = UIAlertAction(title: buttons[index], style: .default, handler: {
(alert: UIAlertAction!) in
if(completion != nil){
completion(index)
}
})
alertController.addAction(action)
}
DispatchQueue.main.async {
vc.present(alertController, animated: true, completion: nil)
}
}
}
If you need to auto dismiss the alert you can call dismiss on presented view controller after some delay.
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) {
vc.dismiss(animated: true, completion: nil)
}
Hope this might help you.