I am trying to segue to another controller by clicking on one of presented options on actionsheet. It works just fine on iPhone screens and it's being pushed to appropriate scenes, however issue occurs on iPad. I have been searching a lot for similar issue, but with no success.
#IBAction func actionSheet(_ sender: UIButton) {
let alert = UIAlertController(title: "Please select one of the options", message: nil, preferredStyle: .actionSheet)
let cancelActionButton = UIAlertAction(title: "Cancel", style: .cancel) { action -> Void in }
let recipeActionButton = UIAlertAction(title: "Get The Recipe", style: .default) { action in self.performSegue(withIdentifier: "GetRecipeID", sender: self) }
let facebookActionButton = UIAlertAction(title: "Login with Facebook", style: .default) { action in self.handleCustomFacebookLogin() }
//actions
alert.addAction(cancelActionButton)
alert.addAction(recipeActionButton)
alert.addAction(facebookActionButton)
// support ipad
if let popoverController = alert.popoverPresentationController {
popoverController.sourceView = sender
popoverController.sourceRect = sender.bounds
}
self.present(alert, animated: true, completion: nil)
}
This approach is also not working:
let viewController = UIStoryboard(name: "Detail", bundle: nil).instantiateViewController(withIdentifier: "DetailsVC") as! DetailsViewController
let recipeActionButton = UIAlertAction(title: "Get The Recipe", style: .default, handler: { action in
self.navigationController?.pushViewController(viewcontroller, animated: true)})
I am getting this warning in console when pushing from iPhone:
pushViewController:animated: called on <UINavigationController
0x7fd96a81f800> while an existing transition or presentation is
occurring; the navigation stack will not be updated.
This is not showing when I trigger action from iPad, but new controller is stacked on top.FirstController, SecondController
after clicking on getRecipe/Login button from previous screen.
You could try to perform the segue from another operation
OperationQueue.main.addOperation {
self.performSegue(withIdentifier: "GetRecipeID", sender: nil)
}
Related
I have a universal master-detail application where I present the master
and detail views side by side on an iPad in both orientations. When a
user makes changes in the detail view on an iPhone I can easily detect
changes and present an alert asking if they want to save the changes or
lose them (CoreData). On an iPad, there is no prohibition against simply clicking
in the master list thereby losing the edits.
I have placed a function in the DetailViewController viewWillDisappear
(for iPad) that raises an alert, but the compiler tells me that it does
not like the presentation of a view on a non-connected view.
Console message: Presenting view controllers on detached view
controllers is discouraged <>.
Is there a more appropriate way to handle this? Swift 3, iOS 10, Xcode 8.2.1
var hasChanged //set to true whenever edits are made
override func viewWillDisappear(_ animated: Bool) {
if UIDevice.current.model == "iPad" {
if hasChanged {
print("hasChanged (should be true) is: \(hasChanged)")
cancelUnsavedEdits()
}//if hasChanged
}//if ipad
}//viewWillDisappear
func cancelUnsavedEdits() {
if hasChanged {
let ac = UIAlertController(title: nil, message: nil, preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "Delete Edits", style: .default, handler: { (action : UIAlertAction!) -> Void in
self.codeDismissTheKeyboard()
self.performSegue(withIdentifier: "unwindToMasterViewController", sender: self)
let editRecordButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.edit, target: self, action: #selector(DetailViewController.editThisRecord))
self.navigationItem.rightBarButtonItem = editRecordButton
self.navigationItem.leftBarButtonItem = nil
self.navigationItem.hidesBackButton = false
//need to remove the edits - refresh the original page
self.configureView()
}))//addAction block
ac.addAction(UIAlertAction(title: "Save Edits", style: .default, handler: { (action : UIAlertAction!) -> Void in
self.codeDismissTheKeyboard()
self.saveTheEditedRecord()
self.performSegue(withIdentifier: "unwindToMasterViewController", sender: self)
}))//addAction block
//ac.addAction(UIAlertAction(title: "Cancel", style: .default, handler: nil))
//try this - for -ipad add code in handler to reopen the fields for editing if the cancel of the cancel is chosen
ac.addAction(UIAlertAction(title: "Cancel", style: .default, handler: { (whatever) in
self.makeEntryFieldsEnabledYES()
let cancelItemButton = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(DetailViewController.cancelUnsavedEdits))
self.navigationItem.leftBarButtonItem = cancelItemButton
}))
//try above
self.present(ac, animated: true, completion: nil)
} else {
self.codeDismissTheKeyboard()
//for ipad
self.navigationItem.rightBarButtonItem = nil
self.performSegue(withIdentifier: "unwindToMasterViewController", sender: self)
}//if hasChanged
//for ipad
navigationItem.leftBarButtonItem = nil
}//cancelUnsavedEdits
I think the problem you try to present a new viewController on a viewController which is going to be removed from the view hierarchy.
Maybe a dirty fix can be to replace:
self.present(ac, animated: true, completion: nil)
to:
self.view.window.rootViewController?.present(ac, animated: true, completion: nil);
How to show or link to new ViewController when push button on alert?
This is my code
let alert = UIAlertController(title: validateQRObj.responseDescription, message: validateQRObj.productName, preferredStyle: .alert)
let action = UIAlertAction(title: "OK", style: .default) { (action) -> Void in
let viewController = self.storyboard?.instantiateViewController(withIdentifier: "ProductDetialViewController")
self.present(viewController!, animated: true, completion: nil)
}
alert.addAction(action)
self.present(alert, animated: true, completion: nil)
Control drag from View Controller 1 (The yellow dot) to any where on View Controller 2 and then click on the Segue. Show the Attributes inspector and Under Storyboard Segue identifier name the identifier VC2
If this is the answer you were looking for don't forget to except the answer.
func alert(){
let alertController = UIAlertController(title: "Open View Controller. ", message: "Press Ok to open View Controller number 2.", preferredStyle: UIAlertControllerStyle.alert)
let ok = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: {(action) -> Void in
//The (withIdentifier: "VC2") is the Storyboard Segue identifier.
self.performSegue(withIdentifier: "VC2", sender: self)
})
alertController.addAction(ok)
self.present(alertController, animated: true, completion: nil)
}
For me it doesn't work. I made :
func alert(){
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let twop = storyboard.instantiateViewController(withIdentifier: "Accueil") as! Accueil
let alertController = UIAlertController(title: "Open new View !", message: "Clic to ok", preferredStyle: UIAlertControllerStyle.alert)
let ok = UIAlertAction(title: "Ok", style: UIAlertActionStyle.default, handler: {(action) -> Void in
self.navigationController?.show(twop, sender: self)
})
alertController.addAction(ok)
self.present(alertController, animated: true, completion: nil)
}
and now it's working !!
I'm trying to perform a transition to another UIViewController in another storyboard but the problem is that the view that I want to display pops behind the view where I'm from.
I know that because my app includes a Snapchat-like navigation between views, so I can drag the view to the left or the right and I can see the view behind.
Here is how I attempt to do that :
#IBAction func account(sender: UIButton) {
if self._isOnline {
self.pageController?.reverseViewControllerAnimated(true)
}
else {
let alertView = UIAlertController(title: "blablablabla", message: "blablablabla", preferredStyle: .Alert)
alertView.addAction(UIAlertAction(title: "No", style: .Cancel, handler: nil))
alertView.addAction(UIAlertAction(title: "Yes", style: .Default, handler: { (alertAction) -> Void in
let storyboard = UIStoryboard(name: "SignUpLogin", bundle: NSBundle.mainBundle())
let vc = storyboard.instantiateInitialViewController() as! UIViewController
self.presentViewController(vc, animated: false, completion: nil)
}))
presentViewController(alertView, animated: true, completion: nil)
}
}
With SignUpLogin that is the name of my second .storyboard file, here I'm in Heart.storyboard.
The action takes place in a UIAlertController, if the user press no, nothing append, if he press yes he's supposed to be redirected to the initial view of my SignUpLogin.storyboard file.
My problem come from the view at the bottom left corner. And if I use the code above in the view at the top right corner, that works ! Why ?
I have no idea of how I can do differently.
I've found the solution to my problem !
#IBAction func account(sender: UIButton) {
if self._isOnline {
self.pageController?.reverseViewControllerAnimated(true)
}
else {
let alertView = UIAlertController(title: Constants.LocalizedJoinOurBand, message: Constants.LocalizedNeedToCreatAnAccount, preferredStyle: .Alert)
alertView.addAction(UIAlertAction(title: Constants.LocalizedNo, style: .Cancel, handler: nil))
alertView.addAction(UIAlertAction(title: Constants.LocalizedYes, style: .Default, handler: { (alertAction) -> Void in
if let appDelegate = UIApplication.sharedApplication().delegate as? AppDelegate {
let storyboard = UIStoryboard(name: "SignUpLogin", bundle: NSBundle.mainBundle())
let vc = storyboard.instantiateInitialViewController() as! UIViewController
UIApplication.sharedApplication().keyWindow?.rootViewController = vc
}
}))
presentViewController(alertView, animated: true, completion: nil)
}
}
I don't know why in this case particularly but I have to directly set the rootViewController of my AppDelegate with this line :
UIApplication.sharedApplication().keyWindow?.rootViewController = vc
I am quite new to Swift development and I tried referring to the Swift UIAlertController API but couldn't figure out how to navigate to another UIViewController after clicking a button on the UIAlertController.
I would appreciate any pointers, help or a solution to this problem. My code snippet is given below -
#IBAction func showAlert() {
let alertController = UIAlertController(title: "Disclaimer", message: "Disclaimer Text Here", preferredStyle: .Alert)
let declineAction = UIAlertAction(title: "Decline", style: .Cancel, handler: nil)
alertController.addAction(declineAction)
let acceptAction = UIAlertAction(title: "Accept", style: .Default) { (_) -> Void in
let secondVC = ViewController(nibName: "ViewController", bundle: nil)
let navController = UINavigationController(rootViewController: secondVC)
self.presentViewController(navController, animated: true, completion: nil)
}
alertController.addAction(acceptAction)
presentViewController(alertController, animated: true, completion: nil)
}
What I am trying to do here is when a button is clicked the disclaimer AlertController is displayed. If "Decline" button is selected, cancel action is performed and if "Accept" is selected the app should navigate to a Navigation Controller which then allows me to navigate to other ViewControllers using a menu in the application.
Earlier I used the story board to link a button to the NavigationController to traverse to the ViewController I desired. Now I want to do the same programmatically for the AlertController "Accept" Button.
Thank you in advance.
You need to implement the handler block to perform code when an action is selected :
#IBActionfunc showAlert() {
let alertController = UIAlertController(title: "Disclaimer", message: "Disclaimer Text Here", preferredStyle: .Alert)
let declineAction = UIAlertAction(title: "Decline", style: .Cancel, handler: nil)
alertController.addAction(declineAction)
let acceptAction = UIAlertAction(title: "Accept", style: .Default) { (_) -> Void in
let secondVC = SecondViewController(nibName: "SecondView", bundle: nil)
let navController = UINavigationController(rootViewController: secondVC)
self.presentViewController(navController, animated: true, completion: nil)
}
alertController.addAction(acceptAction)
presentViewController(alertController, animated: true, completion: nil)
}
Basically to link the UIAlertViewController button to a UINavigationController's UIViewController we would need to create a manual segue between the UIViewController which has the UIAlertViewController to the UINavigationController.
Please refer to this screen shot to see how to do the above -
Then select the link between the UIViewController and the UINavigationController. Go to the left sidebar and select the Attributes inspector and name the identifier.
Now write the following in your code -
#IBAction func showAlert() {
let alertController = UIAlertController(title: "Disclaimer", message: "Before using this teaching resource, you confirm that you agree:\n1. To obey the law regarding data protection and patient confidentiality.\n2. To us this app professionally and appropriately in clinical settings.\n3. This is for your personal use and you may not modify, distribute, publish, transfer any information obtained from this teaching resource without the developers' permission.\n4. In no event shall the developer be liable to you for any loss arising from your use of this resource.", preferredStyle: .Alert)
let declineAction = UIAlertAction(title: "Decline", style: .Cancel, handler: nil)
let acceptAction = UIAlertAction(title: "Accept", style: .Default) { (_) -> Void in
self.performSegueWithIdentifier("SomeSegue", sender: self) // Replace SomeSegue with your segue identifier (name)
}
alertController.addAction(declineAction)
alertController.addAction(acceptAction)
presentViewController(alertController, animated: true, completion: nil)
}
That's the use of the handler block. You should make your desired action in this block.
Edit: Code
let acceptAction = UIAlertAction(title: "Accept", style: .Default, handler:{ action in
//Write your code here
})
You can use this link as a reference: http://www.appcoda.com/uialertcontroller-swift-closures-enum/
The UIAlertViewController shows all my text but on clicking doesn't switch to the other View. Here's the code:
let alertController = UIAlertController(title: "Risposta Esatta", message:"", preferredStyle: UIAlertControllerStyle.Alert)
alertController.addAction(UIAlertAction(title: "Avanti", style:UIAlertActionStyle.Default,handler: {(UIAlertAction) in
let secondViewController:SecondViewController = SecondViewController()
self.presentViewController(secondViewController, animated: true, completion: nil)
}))
This just worked for me:
#IBAction func choiceBtn(sender: UIButton) {
let alertController = UIAlertController(title: "Hello!", message: "This is Alert sample.", preferredStyle: .Alert)
let otherAction = UIAlertAction(title: "OK", style: .Default) {
action in println("pushed OK!")
}
let cancelAction = UIAlertAction(title: "CANCEL", style: .Cancel) {
action in self.performSegueWithIdentifier("VC2", sender: self)
}
alertController.addAction(otherAction)
alertController.addAction(cancelAction)
presentViewController(alertController, animated: true, completion: nil)
}
I created a modal presented segue from VC1 to VC2 and also named the identifier VC2. This code I adapted from the GitHub UIAlertController example here:
https://github.com/eversense/Swift-UIAlertController-Example
There was no segue to another ViewController, so I added that to show you.