How to display AlertController in UiView - ios

new to Swift. Below code is executed without error but in the console it shows a message. Is it an error message and how to fix it? Is it required to be fixed when sending app for review.
Thanks
This is my App structure :
VC(1) --> goto --> VC(2)
In VC(1) I have:
BtnGo
UiLabelMsg
UitableView
I created a Link to VC(2) : Control-Drag BtnGo to VC(2)
I named the segue : SegueToVC2
What I wanted to do:
I need to pass a string to VC2. Before passing check if user made a selection.
in PrepareForSegue,
override func PrepareForSegue(segue:UIStoryboardSegue, sender: AnyObject!) {
var strchk: String = UIlabelMsg.text!
if strchk.isEmpty {
var alert = UIAlertController(title: "Alert",message:" No Selection made",
preferredStyle: UIAlertControllerStyle.Alert)
alert.AddAction(UIAlertction(title: "OK",style:UIAlertActionStyle.Default,hanlder: nil))
self.presentViewController(alert, animated: true, completion: nil )
} else {
if (segue.Identifier =="SegueToVC2") {
var targetVC = segue.destinationViewControl as! VC2
targetVC.strMsg = UILabelMsg.text
}
}
Problem:
in the console, it shows this:
UIView : 0x7fdfc25668c0; frame =(0,0,375,667); autoresize = w+h; layer
= 's windows is not equal to 's View's Window!
I have these questions
How come I don't need to write code for BtnGo when I use prepareForSegue?
if I have more than 2 button? these 2 will handle by Segue?

UIView have not presentViewController method, what's the self? You can using window of UIView to presenting.
let alert = UIAlertController(title: "Alert", message: "No Selection made", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.window?.rootViewController?.present(alert, animated: true, completion: nil)
EDIT: Possible you need to override the shouldPerformSegueWithIdentifier to check the UIlabelMsg.text.
override func shouldPerformSegueWithIdentifier(identifier: String?, sender: AnyObject?) -> Bool {
var strchk: String = UIlabelMsg.text!
if strchk.isEmpty {
var alert = UIAlertController(title: "Alert",message:" No Selection made",
preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK",style:UIAlertActionStyle.Default,handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
return false
} else {
return true
}
}

Related

iOS - UIAlertController actions not triggering handler

I'm extremely new to iOS. I'm trying to show a dialog to the user to get some input, but the actions are never triggered. I've been searching on the net for hours and no answer seem to work for me.
Here's the function I'm trying to use to show the dialog:
private func showAmountDialog(type: String, onComplete: #escaping (Double) -> Void) {
let alert = UIAlertController(title: "Enter an amount", message: nil, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: LzStrings.Common_Cancel, style: .cancel, handler: nil))
alert.addTextField(configurationHandler: { textField in
textField.placeholder = "0.00 \(type)"
textField.keyboardType = .decimalPad
})
alert.addAction(UIAlertAction(title: LzStrings.Common_OK, style: .default) { (UIAlertAction) in
if let input = alert.textFields?.first?.text, let amount = Double(input) {
print("Your amount: \(amount)")
}
})
self.present(alert, animated: true)
}
self here is my ViewController which has a parent of UIViewController type and several other protocols.
What I might be doing wrong?
EDIT: The way I knew it isn't executing is using break-points and not by relying on print("...")
Also, since I added the TextField right before adding the action, the nullability check is useless and the textFields.first is never nil, so in both cases, a break-point should be triggered or the print("...") should be executed, which neither of them is happening.
EDIT 2: Since the if statement can do a little distraction, I edited my code this way and tested again:
alert.addAction(UIAlertAction(title: LzStrings.Common_OK, style: .default) { (UIAlertAction) in
if let input = alert.textFields?.first {
if let amount = Double(input.text ?? "") {
print("Your amount: \(amount)")
} else {
print("Can't cast this string to double")
}
} else {
print("Text field is null")
}
})
Still, no feedback from the dialog.
PS: Even the Cancel button doesn't work.
EDIT 3: My dismiss function is overridden in the super class, but it passes completion closure normally:
override open func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
if let navigationController = self.navigationController as? NavigationController {
navigationController.dismiss(animated: flag, completion: completion)
} else {
super.dismiss(animated: flag, completion: completion)
}
}
After having a conversation with one of my colleagues, we found out that to show standard UIAlertController we must use this:
self.view.window!.rootViewController?.present(alert, animated: true, completion: nil)
Instead of this
self.present(alert, animated: true, completion: nil)
It fixed my issue. I hope someone will find this helpful.
Another option is to use an extention for ViewController:
extension UIViewController {
//Show a basic alert
func showAlert(alertText : String, alertMessage : String) {
let alert = UIAlertController(title: alertText, message: alertMessage, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Got it", style: UIAlertActionStyle.default, handler: nil))
//Add more actions as you see fit
self.present(alert, animated: true, completion: nil)
}
}

unwind segue in UIAlert not working

I am trying to present an alert to the user if they need to be logged in to access a feature but when I press the login button in the alert self.self.performSegueWithIdentifier("tryonToLogin", sender:self) does not appear to do anything.
LoginViewController
#IBAction func unwindToLogin(segue:UIStoryboardSegue){
}
UIAlert
#IBAction func goToGallery(sender: UIButton){
if(isLoggedIn){
performSegueWithIdentifier("ShowGallery", sender: sender)
}else{
showMessage("You must login to view access this feature", sender:sender)
}
}
func showMessage(popUpMessageText : String, sender:UIButton){
let messageTitle = "Error"
print("prepreform segue")
performSegueWithIdentifier("unwindToLogin", sender:sender)
let refreshAlert = UIAlertController(title: messageTitle, message: popUpMessageText, preferredStyle: UIAlertControllerStyle.Alert)
refreshAlert.addAction( UIAlertAction(title: "Login", style: .Default, handler: { (action: UIAlertAction!) in
print("Handle Login redirect logic here")
self.performSegueWithIdentifier("unwindToLogin", sender: self)
print("after Handle Login redirect logic here")
}))
refreshAlert.addAction(UIAlertAction(title: "Cancel", style: .Default, handler: { (action: UIAlertAction!) in
print("Handle Cancel logic here")
}))
presentViewController(refreshAlert, animated: true, completion: nil)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
print("prepare for segue called")
self.camera = nil
}
There are no errors. it is getting called as if ii put in a id that does not exist i get the no segue with specified identifier found error message.
Does the UIAlert affect the calling of the performSegueWithIdentifier()
The following post uses this approach so I think it should work. what am i missing.
**IB Screenshots **
Edit
I have tried moving self.performSegueWithIdentifier("unwindToLogin", sender:self) to view did appear and it still is snot working.
The self inside the block is the UIAlertController, not the UIViewController. Create a weak reference to the UIViewController, and call the method on that object.
weak var weakSelf = self
let refreshAlert = UIAlertController(title: messageTitle, message: popUpMessageText, preferredStyle: UIAlertControllerStyle.Alert)
refreshAlert.addAction( UIAlertAction(title: "Login", style: .Default, handler: { (action: UIAlertAction!) in
print("Handle Login redirect logic here")
dispatch_async(dispatch_get_main_queue()) {
weakSelf?.performSegueWithIdentifier("unwindToLogin", sender:self)
}
}))
Seeing that you have not provide sufficient informations, I present you this 'solution'.
Create an extension of UIViewController in order to implement facility function to implement an alertView-
I suppose that you put your code on "viewDidLoad": you have to move code on "viewDidAppear".
please check what operation you do in prepareForSegue
I think this will be sufficient.
extension UIViewController {
/**
Show alert view with OK/Cancel button.
- parameter title: alert title
- parameter message: alert message
- parameter onOk: callback on ok button tapped
- parameter onCancel: callback on cancel button tapped
- returns: alert controller
*/
func showAlert(title title: String, message: String, onOk: (() -> ())?, onCancel: (() -> ())? = nil) -> UIAlertController {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .Alert)
let okAction = UIAlertAction(title: "Ok", style: .Default) { (alert: UIAlertAction!) -> Void in
onOk?()
}
let cancelAction = UIAlertAction(title: "Cancel"), style: .Default) { (alert: UIAlertAction!) -> Void in
onCancel?()
}
alertController.addAction(okAction)
alertController.addAction(cancelAction)
self.presentViewController(alertController, animated: true, completion: nil)
return alertController
}
}
class LoginViewController: UIViewController {
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let title = "Login"
let message = messageTitle
self.showAlert(title: title, message: message, onOk: { () -> () in
self.performSegueWithIdentifier("unwindToLogin", sender: self)
})
}
}

presentViewController isn't modal in shouldPerformSegueWithIdentifier

I'm trying to make an unwind segue depend on the user approving it in an alert dialog. However, when I run the code the dialog just quickly appears, disappears, and segues to the other view controller. I've tried making the alertDialog an instance variable of the class and that doesn't make a difference. The code below is inside a UIViewController subclass. What am I missing?
Thanks.
override func shouldPerformSegueWithIdentifier(identifier: String, sender: AnyObject?) -> Bool {
var shouldPerformSeque = true
if identifier == "startOver" {
let alertDialog = UIAlertController(title: "Warning",
message: "This will discard all data entered.",
preferredStyle: .Alert)
let okAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
alertDialog.addAction(okAction)
let cancelActionHandler = {
(action: UIAlertAction!) -> Void in
shouldPerformSeque = false
}
let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel,
handler: cancelActionHandler)
alertDialog.addAction(cancelAction)
presentViewController(alertDialog, animated: false, completion: nil)
}
return shouldPerformSeque
}
You can't do this in shouldPerformSegueWithIndentifier because the dialog will be presented asynchronously with regard to that method; by the time the action handler closure executes the method has already returned.
You need to display the dialog in response to whatever is triggering the segue and then perform the unwind programmatically depending on the user's response.

UIAlertController in swift

I am creating a view controller in swift with a few text fields and an accept button which confirms the user's input. The accept button also checks if any of the text fields is empty. If so, it will pop up an alert saying something like it cannot be empty. if it is not empty, it will store the input and then jump to another view.
I created an separated function called checEmpty() which looks like this:
func checEmpty(title: String, object: UITextField) -> (Bool) {
if object.text.isEmpty {
let alertController = UIAlertController(title: "Invalid input",
message:"\(title) cannot be empty",
preferredStyle: UIAlertControllerStyle.Alert)
alertController.addAction(UIAlertAction(title: "Dismiss",
style: UIAlertActionStyle.Default)
self.presentViewController(alertController, animated: true, completion: nil)
return false
} else {
return true
}
}
And I call this function in the acceptButton action:
#IBAction func acceptButton(sender: UIButton){
if(checEmpty("Event", object: eventName) && checEmpty("Priority", object: Priority)
{
//if not empty, confirm the user input
// ...
}
When I run it, the alert message works fine but for some reason the console shows this:
2015-08-03 12:11:50.656 FinishItToday[13777:688070] >'s window is not equal to
's view's window!
Can anyone tell me why this warning appears? Thank you very much!
PS.
What I want it to do is that if any of the text field is empty, show the alert and then stay at the same page. If none of them are empty, then perform the segue and switch to another view. The code above works fine except the warning.
Here is your working code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var eventName: UITextField!
#IBOutlet weak var Priority: UITextField!
#IBAction func acceptButton(sender: UIButton){
if checEmpty("Event", object: eventName) && checEmpty("Priority", object: Priority){
println("Both Text Fields Are Empty")
}
}
func checEmpty(title: String, object: UITextField) -> (Bool) {
if object.text.isEmpty {
var Alert = UIAlertController(title: "Invalid input", message: "\(title) cannot be empty", preferredStyle: UIAlertControllerStyle.Alert)
Alert.addAction(UIAlertAction(title: "Dismiss", style: .Cancel, handler: { action in
println("Click of cancel button")
}))
self.presentViewController(Alert, animated: true, completion: nil)
return false
} else {
return true
}
}
}
Use this code to for alert view controller in swift. It may help you.
import UIKit
protocol alertViewDelegate {
func actionActive(index:Int, tag:Int)
}
class AlertView: NSObject {
var delegate:alertViewDelegate!
func showAlert(title:String, message:String, actionName:NSArray, tag:Int) -> UIAlertController {
var alertController:UIAlertController = UIAlertController(title: title, message: message, preferredStyle: .Alert)
for str: AnyObject in actionName {
let alertAction:UIAlertAction = UIAlertAction(title: str as! String, style: UIAlertActionStyle.Default, handler: { (action) -> Void in
self.delegate.actionActive(actionName.indexOfObject(str), tag:tag)
})
alertController.addAction(alertAction)
}
return alertController;
}
}

Make a class comprises of the functions like UIAlertView, UIActivityIndicator and call them back in various viewControllers

This is my current code:
import UIKit
class classViewController: UIViewController {
// The function i want to call in other view controllers..
func alertView(title: String, message: String) {
var alert:UIAlertController = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: { (action) -> Void in
self.dismissViewControllerAnimated(true, completion: nil)
}))
self.presentViewController(alert, animated: true, completion: nil)
}
}
In the other view controller, where I've made an IBAction to perform this alertView, I have done this:
#IBAction func button(sender: AnyObject) {
classViewController().alertView("title", message: "message")
}
When I run the app, after tapping the button I get this error, but no alertView:
Warning: Attempt to present on
whose view is not in the
window hierarchy!
Right. If you want to make a global class that displays alerts, you need to pass in a reference to the current view controller, and use that instead of "self" in calls like presentViewController.
Your class should probably not be a subclass of UIViewController, since it looks like you're never displaying it to the screen.
I created a Utils class that is a subclass of NSObject.
It has a method showAlertOnVC that looks like this:
class func showAlertOnVC(targetVC: UIViewController?, var title: String, var message: String)
{
title = NSLocalizedString(title, comment: "")
message = NSLocalizedString(message, comment: "")
if let targetVC = targetVC
{
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
let okButton = UIAlertAction(
title:"OK",
style: UIAlertActionStyle.Default,
handler:
{
(alert: UIAlertAction!) in
})
alert.addAction(okButton)
targetVC.presentViewController(alert, animated: true, completion: nil)
}
else
{
println("attempting to display alert to nil view controller.")
println("Alert title = \(title)")
println("Alert message = \(message)")
}
}

Resources