How to unwind segue with alert view in Swift? - iphone

I have several View Controllers. I need to return to the first View Controller if the Alert View is confirmed. This is how I would do it without the unwind Segue:
#IBAction func unwindToDelete ( segue: UIStoryboardSegue ) {
let alertView = UIAlertController(title: "Delete?", message: "Are you sure you wante to delete?", preferredStyle: .ActionSheet)
let deleteAction = UIAlertAction (title: "Delete", style: .Destructive ) { alertAction in
self.deleteChoice = true
}
let cancelAction = UIAlertAction (title: "Cancel", style: .Cancel ) { alertAction in
}
alertView.addAction(deleteAction)
alertView.addAction(cancelAction)
self.presentViewController(alertView, animated: true, completion: nil)
}
But if I do that, in this code it crashes because of the last line of code.
This is the error:
2015-04-30 14:59:45.605 PhotosCollection[4624:182995]
popToViewController:transition: called on <UINavigationController 0x7a67aeb0>
while an existing transition or presentation is occurring; the navigation
stack will not be updated.
How can I complete the Alert View while being able to unwind segue.
Thank you

You are putting up the alert after the unwind has happened. You want to be able to not perform the unwind at all if the user chooses to cancel the delete. I would suggest the following:
Instead of wiring the unwind segue to your delete button, instead connect it to the view controller icon at the top of the view controller so that you can call the unwind programmatically. This answer shows you how to do that:
Setting up the unwind segue.
Give the unwind segue an identifier such as "doUnwind".
In the #IBAction for your delete button, put up the alert asking the user if they really want to delete.
In the handler for the delete button, call the unwind segue programmatically.
#IBAction func deleteButton (button: UIButton) {
let alertView = UIAlertController(title: "Delete?", message: "Are you sure you wante to delete?", preferredStyle: .ActionSheet)
let deleteAction = UIAlertAction (title: "Delete", style: .Destructive ) { alertAction in
self.performSegueWithIdentifier("doUnwind", sender: self)
}
let cancelAction = UIAlertAction (title: "Cancel", style: .Cancel ) { alertAction in
}
alertView.addAction(deleteAction)
alertView.addAction(cancelAction)
self.presentViewController(alertView, animated: true, completion: nil)
}

Related

Unwind segue not dismissing view

The Issue:
Unwind segue not dismissing view, even though it works.
Scenario:
I have an "add product" screen, and when the product is added, I fire up unwind segue, which in turn shows a popup: "New Product added".
The Facts:
1) Product is successfully added to Database
2) the popup "Product added" is shown in the #IBAction of the unwind segue in target, and it appears
3) The popup "Product Added" is shown over the AddProductScreen view
Picture 1: Existence of Unwind segue
#IBAction located in target view:
#IBAction func unwindFromAddProductViewSuccess(segue: UIStoryboardSegue)
{
AlertManager.showTimedAlert(title: "Success", message: "New Product added", timeShowing: 1, callingUIViewController: self)
}
Function being called in AddProductView for registering the product:
private func registerProductAndPerformSegue(productToRegister prod: Product)
{
self.registerProduct(prodToRegister: prod)
Constants.logger.debug("New product registered")
self.performSegue(withIdentifier: "unwind_from_add_product_success", sender: self)
}
In the "Add Product View", after "Confirm" is clicked, an alert shows with "Are you sure?" and then you are prompt to click "Yes" or "No".
This is the code for the alert:
AlertManager.ShowAlert(controllerToShowAlert: self, alertTitle: LocalizationStrings.Products.ADD_PRODUCT_TITLE, alertText: LocalizationStrings.Products.ADD_PRODUCT_CONFIRMATION,
alertStyle: UIAlertControllerStyle.alert,
leftButtonAction: UIAlertAction(title: LocalizationStrings.YES_TEXT, style: .default, handler:
{
(action: UIAlertAction!) in
let user = Auth.auth().currentUser
if let user = user
{
let product : Product = Product(name: name!, price: Double(price!)!, currency: "USD", description: desc!,
location: "USA", ownerID: user.uid, ownerName: user.displayName!, uniqueID: "", mainImageURL: nil, category: category)
let storageRef = Storage.storage().reference().child(product.getUniqueID()).child("pic0.jpg")
if let mainChosenImage = self.selectedImageToUpload
{
Constants.logger.debug("Product picture chosen")
if let uploadData = UIImageJPEGRepresentation(mainChosenImage, 0.2)
{
storageRef.putData(uploadData, metadata: nil)
{
(StorageMetaData, error) in
if error != nil
{
Constants.logger.error("Add Product: Couldn't store image in database!")
return
}
self.mainImageURL = StorageMetaData?.downloadURL()?.absoluteString
if let urlString = self.mainImageURL
{
product.AddImageURLToProduct(URL: urlString)
self.registerProductAndPerformSegue(productToRegister: product)
}
}
}
else
{
Constants.logger.error("Couldn't convert uploaded image to UIImageJPEGRepresentation")
}
}
else
{
Constants.logger.debug("Product picture NOT chosen")
self.registerProductAndPerformSegue(productToRegister: product)
}
}
}),
rightButtonAction: UIAlertAction(title: LocalizationStrings.NO_TEXT, style: .cancel, handler: { (action: UIAlertAction!) in
print("Handle Cancel Logic here")
}))
What am I doing wrong here?
Without more details about your Storyboard setup and your code, it's hard to tell what the exact problem is.
But I'll assume that you have two view controllers and I will try to guide you through the steps to do an unwindSegue and you can check if you maybe have missed something.
Assuming that you have two view controllers: FirstViewController andAddProductViewController, andAddProductViewController` is presented using a push segue.
Inside FirstViewController you have:
#IBAction func unwindFromAddProductViewSuccess(segue: UIStoryboardSegue) {
AlertManager.showTimedAlert(title: "Success", message: "New Product added", timeShowing: 1, callingUIViewController: self)
}
Then, You connect your "save" or "add" button from AddProductViewController to unwindFromAddProductViewSuccess: action method.
You can also connect your "save" or "add" button to SecondViewController to debug that the segue is being called:
class SecondViewController: UIViewController {
#IBOutlet weak var saveButton: UIBarButtonItem!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let uiBarButtonItem = sender as? UIBarButtonItem else {
print("There is no UIBarButtonItem sender")
return
}
if saveButton == uiBarButtonItem {
print("save button tapped")
}
print("A segue with an identifier \(segue.identifier ?? "") was called.")
}
}
Assuming that you don' have a bar button item and want to call unwidnSegue manually, maybe after the user taps on an UIAlertController action, then you can do something like this:
#IBAction func orderButtonTapped(_ sender: UIButton) {
let alert = UIAlertController(title: "Order Placed!", message: "Thank you for your order.\nWe'll ship it to you soon!", preferredStyle: .alert)
let OKAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: {
(_)in
self.performSegue(withIdentifier: "unwindToMenu", sender: self)
})
alert.addAction(OKAction)
self.present(alert, animated: true, completion: nil)
}
There are also some cases when the UIViewController that you are trying to unwind from is at the bottom of the UINavigationController stack, in this case, calling unwindSegue will not do anything. There was a similar question related to that before, here.
I hope that this info can help you figure out the problem.

Action sheet does not work in iPhone simulator

I am trying to implement the Action sheet in Swift. Below is the code to implement it. When i execute the code, Xcode does not show any errors and the action sheet does not appear in the simulator. Any help in resolving the issue is much appreciated.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
func showActionSheet(sender: AnyObject) {
let actionSheetController: UIAlertController = UIAlertController(title: "Action Sheet", message: "Choose an option!", preferredStyle: .actionSheet)
//Create and add the Cancel action
let cancelAction: UIAlertAction = UIAlertAction(title: "Cancel", style: .cancel) { action -> Void in
//Just dismiss the action sheet
}
actionSheetController.addAction(cancelAction)
self.present(actionSheetController, animated: true, completion: nil)
The way your code is provided, it looks as if you added a method/function inside viewWillAppear. If that is not a typing mistake and you did indeed set your code up like that, modifying your code to look like this will get it to work:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let actionSheetController: UIAlertController = UIAlertController(title: "Action Sheet", message: "Choose an option!", preferredStyle: .actionSheet)
//Create and add the Cancel action
let cancelAction: UIAlertAction = UIAlertAction(title: "Cancel", style: .cancel) { action -> Void in
//Just dismiss the action sheet
}
actionSheetController.addAction(cancelAction)
self.present(actionSheetController, animated: true, completion: nil)
}
Basically, do not have a method inside viewWillAppear but also move your action sheet display code to viewDidAppear instead of viewWillAppear

UIAlertController is disappearing automatically after short time

If a textfield is empty, I would like to make an UIAlertController to remind, that something has to be filled out in the textfield. Now I have the following code:
#IBAction func saveDetails(segue: UIStoryboardSegue) {
let dController = segue.source as? EntryTableViewController
if let text = dController?.bezeichnungTextField.text, text.isEmpty {
let alert = UIAlertController(title: "No description", message: "Please fill out description", preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "OK", style: .default)
alert.addAction(cancelAction)
self.present(alert, animated: true, completion: nil)
}else {
guard let menge = dController?.mengeTextField.text, let bezeichnung = dController?.bezeichnungTextField.text, let kategorie = dController?.categoryButtonOutlet.titleLabel?.text else {return}
self.saveItem(menge: menge, bezeichnung: bezeichnung, kategorie: kategorie)
self.tableView.reloadData()
}
}
Now actually it works when I press the button to return to the first controller. But the UIAlertController only appears for a very short moment and then disappears automatically. Is there a mistake in my code or isn't it possible to call the UIAlertController on a unwind segue?
Thank you for your help
You just show the alert controller and then immediately unwind. The solution is to check for the empty textfield before unwinding.
Make IBAction for your button then check the textfield if it is empty and then perforn your segue programmatically.

How to get back to viewcontroller in alertview

I tried to get back to the viewcontroller when user press Go home in the alertView and it show me the error ( libc++abi.dylib: terminating with uncaught exception of type NSException )
#IBAction func roundButton(sender: AnyObject) {
//add alert
alertController = UIAlertController(title: "Return Home", message: "Are you sure???", preferredStyle: .Alert)
let alertAction1 = UIAlertAction(title: "Cancel", style: .Default) { (action) in
}
let alertAction2 = UIAlertAction(title: "Go Home", style: .Destructive) { (action) in
let nv = self.storyboard!.instantiateViewControllerWithIdentifier("storyboardidentifier") as! ViewController
self.presentViewController(nv, animated:true, completion:nil)
}
alertController?.addAction(alertAction2)
alertController?.addAction(alertAction1)
self.presentViewController(alertController!, animated: true, completion: nil)
//end alert
}
Since you didn't provide much information, there are a lot of possibilities. I will try to cover most of them.
Let's say that you are presenting the alert in BViewController. And you want to get back to AViewController.
If AViewController and BViewController are embedded in a UINavgationController, you will have to connect an unwind segue between BViewController and AViewController.
First, in AViewController, add this method:
#IBAction func unwind(segue: UIStoryBoardSegue) {
}
In the storyboard, control-drag from BViewController to the "Exit" of `AviewController and select "unwind:" aka the method you just declared. This is what you should see
Now give the segue you just added an identifier, say "unwindToHome".
In the UIAlertAction call back closure, initiate the segue:
self.performSegueWithIdentifier("unwindToHome", sender: self)
And that's it!
If you are presenting the BViewController modally, then things are simpler, just call dismissViewControllerAnimated:
self.dismissViewControllerAnimated(true, completion: nil)
Create a segue on your storyboard between the two view controllers and give it an Identifier, then, on the completion handler of your alert action, you can trigger the segue. Something along the lines of:
let alertAction2 = UIAlertAction(title: "Go Home", style: .Destructive) { (action) in
self.performSegueWithIdentifier("MyStoryboardSegue", sender: self)
}
Good luck!

Creating a pop up dialog alert

I'm trying to create a popup uialert sort of to create a confirmation box. I have a button in ViewControllerTwo and when pressed navigates back to ViewControllerOne, however I want to create a popup message that asks to confirm (Yes or No) if I really want to navigate to ViewControllerOne. If yes it goes backs to ViewOne, if no it stays on the ViewTwo. How do I do this?
#IBAction func showAlertTapped(sender: AnyObject) {
//Create the AlertController
let myAlertController: UIAlertController = UIAlertController(title: "Hey..!", message: "Are You sure to Do some stuff??", preferredStyle: .Alert)
//Create and add the Cancel action
let cancelAction: UIAlertAction = UIAlertAction(title: "Cancel", style: .Cancel) { action -> Void in
//Do some stuff
}
myAlertController.addAction(cancelAction)
//Create and an option action
let nextAction: UIAlertAction = UIAlertAction(title: "Next", style: .Default) { action -> Void in
let mainStoryboard = UIStoryboard(name: "Storyboard", bundle: NSBundle.mainBundle())
let vc : UIViewController = mainStoryboard.instantiateViewControllerWithIdentifier("vcMainLogin") as UIViewController
self.presentViewController(vc, animated: true, completion: nil)
}
myAlertController.addAction(nextAction)
//Present the AlertController
self.presentViewController(myAlertController, animated: true, completion: nil)
}