Cant dismiss my previous alertcontroller completely - ios

I am trying to use one single alert controller which include multiple alert showing functions and one dismiss function.But I am having this warning in my console and my other alert don't show.I wonder why?and the solution for that.
Here is my alert controller
import UIKit
class MyAlertViewController: UIViewController {
var myAlertController : UIAlertController!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func displayLoadingAlert(viewController: UIViewController?) -> UIAlertController {
var controllerToPresent = viewController
if controllerToPresent == nil {
controllerToPresent = self
}
//create an alert controller
myAlertController = UIAlertController(title: "Loading...", message: “We are receiving data,please wait“, preferredStyle: .ActionSheet)
controllerToPresent!.presentViewController(myAlertController, animated: true, completion: nil)
return myAlertController
}
func connectionErrorAlert(viewController: UIViewController?) -> UIAlertController {
var controllerToPresent = viewController
if controllerToPresent == nil {
controllerToPresent = self
}
//create an alert controller
myAlertController = UIAlertController(title: "Not Connected", message: “No internet Connection”, preferredStyle: .Alert)
let defaultAction = UIAlertAction(title: "OK", style: .Default,handler:nil)
myAlertController.addAction(defaultAction)
controllerToPresent!.presentViewController(myAlertController, animated: true, completion: nil)
return myAlertController
}
func requestTimeOutErrorAlert(viewController: UIViewController?) -> UIAlertController {
var controllerToPresent = viewController
if controllerToPresent == nil {
controllerToPresent = self
}
//create an alert controller
myAlertController = UIAlertController(title: "Request Time Out", message: “Please Click Retry”, preferredStyle: .Alert)
let defaultAction = UIAlertAction(title: "OK", style: .Default,handler:nil)
myAlertController.addAction(defaultAction)
controllerToPresent!.presentViewController(myAlertController, animated: true, completion: nil)
return myAlertController
}
func dismissLoadingAlert(){
myAlertController.dismissViewControllerAnimated(true, completion: nil)
}
}
I use to do dismissLoadingAlert() when I get the results from API.But,when I don't get the results from API.i used to do this delegate method from my protocol.
func didNotReceiveAPIResults(results: Bool,error:NSError){
dispatch_async(dispatch_get_main_queue(), {
// This condition will be enter if we dont get the results and show user with alert.
if (results) {
// I have done dismiss first data receiving alert,what do i doing wrong?
self.myAlertController.dismissLoadingAlert()
if error.localizedDescription == "The Internet connection appears to be offline."{
self.myAlertController.connectionErrorAlert(self)
self.carTableView.hidden=true
self.retryButton?.hidden=false
self.retryButton?.enabled=true
}
if error.localizedDescription == "Request Time Out."{
self.myAlertController.requestTimeOutErrorAlert(self)
self.carTableView.hidden=true
self.retryButton?.hidden=false
self.retryButton?.enabled=true
}
}else{
self.myAlertController.displayLoadingAlert(self)
self.retryButton?.hidden=true
self.retryButton?.enabled=false
self.carTableView.hidden=false
self.carTableView.reloadData()
}
})
}

You are on the wrong track. You should not subclass this controller.
The UIAlertController class is intended to be used as-is and does not support subclassing. The view hierarchy for this class is private and must not be modified.
If you need a convenience class, refactor using a NSObject subclass that presents dynamic UIAlertController instances.

Related

How to create a reusable alert view used across different view controllers?

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

Show and dismis UIAlertController in Swift 4

I declared a global variable for UIAlertViewController for me to be able to show and dismiss it in different method inside my class.
I displayed two kinds of alert: First, alert with button which will be displayed when an error is encountered or to display an information message. Second is an alert without button which will be displayed like a progress message.
Here is the sample code:
private var alert: UIAlertController? // global declaration
private func showProgressMessage(sender viewController: UIViewController, message alertMessage: String)
{
DispatchQueue.main.async
{
self.alert= UIAlertController(title: "", message: alertMessage, preferredStyle: .alert)
viewController.present(self.alert!, animated: true, completion: nil)
}
}
private func showAlertMessage(sender viewController: UIViewController, title alertTitle: String, message alertMessage: String)
{
DispatchQueue.main.async
{
self.alert= UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .alert)
self.alert!.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
viewController.present(self.alert!, animated: true, completion: nil)
}
}
private func method1()
{
DispatchQueue.global().async
{
// some code here
self.showProgressMessage(sender: self, message: "Processing...")
// some code here
}
}
private func method2()
{
// some code here
self.alert!.dismiss(animated: false)
{
self.showAlertMessage(sender: self, message: "Done")
}
self.displayOtherViewController()
}
private func displayOtherViewController()
{
self.alert?.dismiss(animated: false)
{
if let viewController = self.storyboard?.instantiateViewController(withIdentifier: "Sample")
{
let view = viewController as! SampleViewController
view .modalTransitionStyle = .crossDissolve
self.present(view , animated: true, completion: nil)
}
}
}
In method2, displaying the alert again will take a few seconds to display, same with the view controller.
What is the proper way to show and dismis the UIAlertController in Swift 4?
Seems like your code is initiated from a background thread.
Even dismiss must be called on main thread
Try this:
private func method2() {
DispatchQueue.main.async {
self.alert!.dismiss(animated: false) {
self.showAlertMessage(sender: self, message: "Done")
}
self.displayOtherViewController()
}
}

Attempt to present UIAlertController whose view is not in the window hierarchy - Swift Error

I'm trying to create an alert so when a user signs up and then wants to log back in they can be warned if the password is wrong because at the moment it just performs the segue and the attempt to alert fails. I'm using Firebase so the password that is entered into firebase on sign up should be the one users log in with.
import Foundation
import UIKit
import Firebase
class SignInViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var EmailAddressTextField: UITextField!
#IBOutlet weak var PasswordTextField: UITextField!
// Do any additional setup after loading the view.
override func viewDidLoad() {
super.viewDidLoad()
EmailAddressTextField.delegate = self
PasswordTextField.delegate = self
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
#IBAction func LogInButton(_ sender: Any) {
if (EmailAddressTextField.text != "" && PasswordTextField.text != ""){
Auth.auth().signIn(withEmail: EmailAddressTextField.text!, password: PasswordTextField.text!) { user, error in
if error == nil {
self.performSegue(withIdentifier: "LogInSegue", sender: nil)
} else {
let alert = UIAlertController(title: "Error", message: "Enter a correct email and password", preferredStyle: .alert)
let action = UIAlertAction(title: "OK", style: .default, handler: nil)
alert.addAction(action)
self.present(alert, animated: true, completion: nil)
}
}
}
}
}
Just want to check something with you, as your description above is a little unclear. When you enter the wrong email/password combination, does your app still perform the "LogInSegue" (as well as failing to show the alert)?
If so, it sounds like you might have connected your segue to the UIButton instead of the UIViewController.
To check this, click on your segue in the Storyboard and see if the UIButton is highlighted.
If it is, then delete this segue and follow the instruction below to connect your new segue from the view controller:
You are presenting UIAlertController on SignInViewController so at that time SignInViewController is not in navigation stack.
So when you are presenting UIAlertController at that time check the self.navigationController?.viewControllers and verify that it is in stack or not.
for eg.
//check before self.presnt...
print(self.navigationController?.viewControllers)
So you need to make sure that when you are presenting a view controller over another view controller that must be in navigation stack otherwise you will get this message in log.
Attempt to present UIAlertController whose view is not in the window hierarchy
Use the following function to show alert over root view controller
func showAlert(title: String, msg: String, actions:[UIAlertAction]?) {
var actions = actions
let alertVC = UIAlertController(title: title, message: msg, preferredStyle: .alert)
if actions == nil {
actions = [UIAlertAction(title: "OK", style: .default, handler: nil)]
}
for action in actions! {
alertVC.addAction(action)
}
if let rootVC = UIApplication.shared.delegate?.window??.rootViewController {
rootVC.present(alertVC, animated: true, completion: nil)
} else {
print("Root view controller is not set.")
}
}
Usage
self.showAlert(title: "My App", msg: "your message", actions: nil)
You need to use this with your code like this...
class SignInViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var EmailAddressTextField: UITextField!
#IBOutlet weak var PasswordTextField: UITextField!
// Do any additional setup after loading the view.
override func viewDidLoad() {
super.viewDidLoad()
EmailAddressTextField.delegate = self
PasswordTextField.delegate = self
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
#IBAction func LogInButton(_ sender: Any) {
if (EmailAddressTextField.text != "" && PasswordTextField.text != ""){
Auth.auth().signIn(withEmail: EmailAddressTextField.text!, password: PasswordTextField.text!) { user, error in
if error == nil {
self.performSegue(withIdentifier: "LogInSegue", sender: nil)
} else {
// let alert = UIAlertController(title: "Error", message: "Enter a correct email and password", preferredStyle: .alert)
// let action = UIAlertAction(title: "OK", style: .default, handler: nil)
// alert.addAction(action)
// self.present(alert, animated: true, completion: nil)
//use like this...
self.showAlert(title: "Error", msg: "Enter a correct email and password", actions: nil)
}
}
}
}
//###############################################
func showAlert(title: String, msg: String, actions:[UIAlertAction]?) {
var actions = actions
let alertVC = UIAlertController(title: title, message: msg, preferredStyle: .alert)
if actions == nil {
actions = [UIAlertAction(title: "OK", style: .default, handler: nil)]
}
for action in actions! {
alertVC.addAction(action)
}
if let rootVC = UIApplication.shared.delegate?.window??.rootViewController {
rootVC.present(alertVC, animated: true, completion: nil)
} else {
print("Root view controller is not set.")
}
}
//###############################################
}
Present it in main activity :
dispatch_async(dispatch_get_main_queue(), ^{
let alert = UIAlertController(title: "Error", message: "Enter a correct email and password", preferredStyle: .alert)
let action = UIAlertAction(title: "OK", style: .default, handler: nil)
alert.addAction(action)
self.present(alert, animated: true, completion: nil)
});

How to create uialertcontroller in global swift

I'm trying to create uialertcontroller in Config.swift file as follow.
static func showAlertMessage(titleStr:String, messageStr:String) -> Void {
let window : UIWindow?
let alert = UIAlertController(title: titleStr, message: messageStr, preferredStyle: UIAlertControllerStyle.Alert);
self.window!.presentViewController(alert, animated: true, completion: nil)
}
problem is I've found problem in self.window!.
Type 'Config' has no member 'window'
Please let me know how to solve that issue.
swift
Here's what I used, this is the same as #penatheboss answered, just add the ability of adding actions and handlers.
extension UIViewController {
func popupAlert(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)
}
}
Just make sure actionTitles and actions array the same count. Pass nil if you don't need any action handler closure.
self.popupAlert(title: "Title", message: " Oops, xxxx ", actionTitles: ["Option1","Option2","Option3"], actions:[{action1 in
},{action2 in
}, nil])
Objective C:
Add the category for UIViewController
UIViewController+PopAlert.h
#import <UIKit/UIKit.h>
#interface UIViewController (UIViewControllerCategory)
- (void) popAlertWithTitle: (NSString*) title message: (NSString*) message actionTitles:(NSArray *) actionTitles actions:(NSArray*)actions;
#end
UIViewController+PopAlert.m
#import "UIViewController+PopAlert.h"
#implementation UIViewController (UIViewControllerCategory)
- (void) popAlertWithTitle: (NSString*) title message: (NSString*) message actionTitles:(NSArray *) actionTitles actions:(NSArray*)actions {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
[actionTitles enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
UIAlertAction *action = [UIAlertAction actionWithTitle:obj style:UIAlertActionStyleDefault handler:actions[idx]];
[alert addAction:action];
}];
[self presentViewController:alert animated:YES completion:nil];
}
#end
Usage:
#import UIViewController+PopAlert.h
...
[super viewDidDisappear:animated];
NSArray *actions = #[^(){NSLog(#"I am action1");}, ^(){NSLog(#"I am action2");}];
[self popAlertWithTitle:#"I am title" message:#"good" actionTitles:#[#"good1", #"good2"] actions:actions];
self.window would mean that there's a window object in this class, and it's not the case.
You would need to use your let window : UIWindow? with window?.presentViewController(alert, animated: true, completion: nil), but this won't help, since this window does not actually represent any existing window, and it's not a view controller anyway.
So I suggest you pass the actual view controller you'll be using to the method:
static func showAlertMessage(vc: UIViewController, titleStr:String, messageStr:String) -> Void {
let alert = UIAlertController(title: titleStr, message: messageStr, preferredStyle: UIAlertControllerStyle.Alert);
vc.presentViewController(alert, animated: true, completion: nil)
}
and you call it from a class where a UIViewController object is available.
Details
Swift 5.1, Xcode 11.3.1
Global UIAlertController With UIViewController Extension
extension UIViewController{
// Global Alert
// Define Your number of buttons, styles and completion
public func openAlert(title: String,
message: String,
alertStyle:UIAlertController.Style,
actionTitles:[String],
actionStyles:[UIAlertAction.Style],
actions: [((UIAlertAction) -> Void)]){
let alertController = UIAlertController(title: title, message: message, preferredStyle: alertStyle)
for(index, indexTitle) in actionTitles.enumerated(){
let action = UIAlertAction(title: indexTitle, style: actionStyles[index], handler: actions[index])
alertController.addAction(action)
}
self.present(alertController, animated: true)
}
}
Usage
Alert
self.openAlert(title: "alert",
message: "add your message",
alertStyle: .alert,
actionTitles: ["Okay", "Cancel"],
actionStyles: [.default, .cancel],
actions: [
{_ in
print("okay click")
},
{_ in
print("cancel click")
}
])
ActionSheet
self.openAlert(title: "actionsheet",
message: "add your message",
alertStyle: .actionSheet,
actionTitles: ["Okay", "Cancel"],
actionStyles: [.default, .cancel],
actions: [
{_ in
print("okay click")
},
{_ in
print("cancel click")
}
])
I suggest creating an extension:
extension UIViewController {
func showAlertMessage(titleStr:String, messageStr:String) {
let alert = UIAlertController(title: titleStr, message: messageStr, preferredStyle: UIAlertControllerStyle.Alert)
self.presentViewController(alert, animated: true, completion: nil)
}
}
You can try this, please add below code in AppDelegate.swift file.
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)
}
and call showAlertView method from any viewcontroller
AppDelegate.showAlertView(vc: self, titleString: "testTitle", messageString: "test msg")
I am suggest you write this code, but if you really need, try this:
static func showAlertMessage(titleStr:String, messageStr:String) -> Void {
let alert = UIAlertController(title: titleStr, message: messageStr, preferredStyle: UIAlertControllerStyle.Alert);
if let viewController = UIApplication.sharedApplication().windows.first?.rootViewController as UIViewController? {
viewController.presentViewController(alert, animated: true, completion: nil)
}
}
At least it won't break down.
#Eric D better answer.
I created a alerMessage class .I can call any where in my application
//Common Alert Message Class
class AlertMessage {
internal static var alertMessageController:UIAlertController!
internal static func disPlayAlertMessage(titleMessage:String, alertMsg:String){
AlertMessage.alertMessageController = UIAlertController(title: titleMessage, message:
alertMsg, preferredStyle: UIAlertControllerStyle.Alert)
AlertMessage.alertMessageController.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default,handler: nil))
if let controller = UIApplication.sharedApplication().keyWindow?.rootViewController?.presentedViewController {
controller.presentViewController(AlertMessage.alertMessageController, animated: true, completion: nil)
}
else{
UIApplication.sharedApplication().delegate?.window!!.rootViewController?.presentViewController(AlertMessage.alertMessageController, animated: true, completion: nil)
}
return
}
}
Please refer the below GIT Example
https://github.com/amilaim/CommonAlertView
// ViewController.swift
// CommonAlertView
//
// Created by Amila Munasinghe on 4/25/17.
// Copyright © 2017 Developer Insight. All rights reserved.
//
import UIKit
class ViewController: UIViewController,AlertViewControllerDelegate {
#IBOutlet weak var AlertViewResultTextOutlet: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func ShowAlertAction(_ sender: Any) {
let alert = AlertViewController.sharedInstance
alert.delegate = self
alert.SubmitAlertView(viewController: self,title: "Developer Insight", message: "Please enter any text value")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func SubmitAlertViewResult(textValue : String) {
AlertViewResultTextOutlet.text = textValue
}
}
Common UIAlertViewController Implementation
import UIKit
protocol AlertViewControllerDelegate {
func SubmitAlertViewResult(textValue : String)
}
class AlertViewController {
static let sharedInstance = AlertViewController()
private init(){}
var delegate : AlertViewControllerDelegate?
func SubmitAlertView(viewController : UIViewController,title : String, message : String){
let alert = UIAlertController(title: title,
message: message,
preferredStyle: .alert)
// Submit button
let submitAction = UIAlertAction(title: "Submit", style: .default, handler: { (action) -> Void in
// Get 1st TextField's text
let textField = alert.textFields![0]
if(textField.text != "")
{
self.delegate?.SubmitAlertViewResult(textValue: textField.text!)
}
})
// Cancel button
let cancel = UIAlertAction(title: "Cancel", style: .destructive, handler: { (action) -> Void in })
// Add 1 textField and cutomize it
alert.addTextField { (textField: UITextField) in
textField.keyboardAppearance = .dark
textField.keyboardType = .default
textField.autocorrectionType = .default
textField.placeholder = "enter any text value"
textField.clearButtonMode = .whileEditing
}
// Add action buttons and present the Alert
alert.addAction(submitAction)
alert.addAction(cancel)
viewController.present(alert, animated: true, completion: nil)
}
}
What I'm using based on the solution by #William Hu:
func popup(caller:UIViewController, style:UIAlertControllerStyle? = UIAlertControllerStyle.alert,
title:String, message:String, buttonTexts:[String], buttonStyles:([UIAlertActionStyle?])? = nil,
handlers:[((UIAlertAction) -> Void)?], animated:Bool? = nil, completion: (() -> Void)? = nil) {
let alert = UIAlertController(title: title, message: message, preferredStyle: style!)
for i in 0..<buttonTexts.count {
alert.addAction(UIAlertAction(title: buttonTexts[i],
style: (buttonStyles == nil || i >= buttonStyles!.count || buttonStyles![i] == nil ?
UIAlertActionStyle.default : buttonStyles![i]!),
handler: (i >= handlers.count || handlers[i] == nil ? nil : handlers[i]!)))
}
caller.present(alert, animated: animated != nil ? animated! : true, completion: completion)
}
Single function gives Alert by default and can optionally be used for ActionSheet.
Arrays buttonTexts, buttonStyles and handlers can be of unequal sizes as per requirement.
Actions can be styled.
Animated can be specified.
Optional block can be specified to be executed when presentation finishes.
Usage:
popup(caller: self, style: UIAlertControllerStyle.alert,
title: "Title", message: "Message",
buttonTexts: ["Destructive", "Cancel", "OK"],
buttonStyles: [UIAlertActionStyle.destructive, UIAlertActionStyle.cancel],
handlers: [nil], animated: false)
You can use my Utility class created for Show Alert in Swift4. Its super easy to use just by writing single line of code:
Show Simple Alert
#IBAction func showDefaultAlert(_ sender: Any) {
Alert.showAlert(title:"Alert", message:"Default Alert")
}
Demo code link: https://github.com/smindia1988/EasyAlertInSwift4
If you want to present from AppDelegate windows you can use like this
UIApplication.sharedApplication().delegate?.window.rootViewController?.presentViewController(vc, animated: true, completion: nil)
This is also the way you can present on top of the View Controller available.
UIApplication.topViewController()?.present(alertViewController!, animated: true, completion: nil)

iOS 8 Swift 1.2 and Parse - Attempt to present UIAlertController on ViewController whose view is not in the window hierarchy

There are many questions on Stack concerning this issue, but none of them seem to solve the issue I'm having.
I am using ParseUI for the login and signup portion of my application. What I would like to have happen is for a UIAlertController to be presented when a user (for example) does not enter in any text in the username and password fields.
Here is the code for my MasterViewController:
class MasterViewController: UIViewController, PFLogInViewControllerDelegate,
PFSignUpViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if (PFUser.currentUser() == nil) {
var logInViewController = LoginViewController()
logInViewController.delegate = self
var signUpViewController = SignUpViewController()
signUpViewController.delegate = self
logInViewController.signUpController = signUpViewController
self.presentViewController(logInViewController, animated: true, completion: nil)
}
}
func logInViewController(logInController: PFLogInViewController,
shouldBeginLogInWithUsername username: String, password: String) -> Bool {
if (!username.isEmpty || !password.isEmpty) {
return true
} else {
let alertController = UIAlertController(title: "Failed to login.",
message: "Login Failure. Please try again.", preferredStyle: .Alert)
let defaultAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
alertController.addAction(defaultAction)
self.presentViewController(alertController, animated: true, completion: nil)
return false
}
}
func logInViewController(logInController: PFLogInViewController,
didFailToLogInWithError error: NSError?) {
println("Failed to log in.")
}
func signUpViewController(signUpController: PFSignUpViewController,
shouldBeginSignUp info: [NSObject : AnyObject]) -> Bool {
if let password = info["password"] as? String {
return count(password.utf16) >= 8
} else {
return false
}
}
func signUpViewController(signUpController: PFSignUpViewController,
didFailToSignUpWithError error: NSError?) {
println("Failed to sign up.")
}
func logInViewController(logInController: PFLogInViewController,
didLogInUser user: PFUser) {
let installation = PFInstallation.currentInstallation()
installation["user"] = PFUser.currentUser()
installation.saveInBackground()
self.dismissViewControllerAnimated(true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func signUpViewControllerDidCancelSignUp(signUpController:
PFSignUpViewController) {
println("User dismissed signup.")
}
}
After reading another user's answer which seemed like it would be the answer, I added the following class to my workspace:
import Foundation
class AlertHelper: NSObject {
func showAlert(fromController controller: MasterViewController) {
var alert = UIAlertController(title: "abc", message: "def", preferredStyle: .Alert)
controller.presentViewController(alert, animated: true, completion: nil)
}
}
And then modified my method accordingly:
func logInViewController(logInController: PFLogInViewController,
shouldBeginLogInWithUsername username: String, password: String) -> Bool {
if (!username.isEmpty || !password.isEmpty) {
return true
} else {
let alertController = UIAlertController(title: "Failed to login.",
message: "Login Failure. Please try again.", preferredStyle: .Alert)
let defaultAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
alertController.addAction(defaultAction)
var alert = AlertHelper()
alert.showAlert(fromController: self)
return false
}
}
At this point I'm not quite sure what else to do. One thought I had was to programmatically add a UILabel to my LoginViewController and SignUpViewController and change the content based on the errors (or lack thereof) for the user login and signup, but I would like to have alerts.
EDIT: This is the code in my LoginViewController. It subclassed in order to customize the appearance. The code for my SignUpViewController is almost identical.
import UIKit
import Parse
import ParseUI
class LoginViewController: PFLogInViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = ckPurple
let label = UILabel()
label.textColor = UIColor.whiteColor()
label.text = "Welcome."
label.sizeToFit()
logInView?.logo = label
// Do any additional setup after loading the view.
}
The problem is that you present a login view controller from your master view controller, which removes the master view controller from the view hierarchy. You then attempt to present an alert view controller from your master view controller, but you need to present from a view controller in the view hierarchy. Try presenting the alert from your login view controller instead.
loginController.presentViewController(alertController, animated: true, completion: nil)

Resources