How to segue across iOS Navigation Controller programmatically using Swift? - swift

My question is very much like these two:
IOS - How to segue programmatically using swift
How do I create a segue that can be called from a button that is created programmatically?
EXCEPT that my segue is to a Navigation Controller, like this: Main->NC->VC2>VC3.
The segue from Main to VC2 works fine when I trigger it with a button in my first view controller. It does not work when the segue is called by code. I need to have the segue triggered automatically in response to certain conditions determined by the code in Main.
When the button is linked to a function coded in Main, it still works. Code in the button function definition runs and the segue occurs. When I try to trigger the segue in the code for the function, using
performSegueWithIdentifier("showHelp", sender: nil)
or
performSegueWithIdentifier("showHelp", sender: self)
I get an error:
Warning: Attempt to present <UINavigationController: 0x7fc8e1043000> on <myApp.Main: 0x7fc8e0d17b10> whose view is not in the window hierarchy!
The button continues to trigger the segue, despite the warning, but not when the button's function is called by other code.

self.navigationController?.pushViewController(nextViewController, animated: true)

Related

Swift - Run IBAction before unwinding from view controller

So I have a "Save" button on a view controller, we'll call VC2. When clicked, it unwinds back the original view controller, we'll call VC1.
However, I want to run some logic on the data from VC2 before I pass it back to VC1. I tried mapping an IBAction from the Save button that exits VC2 and runs the unwind code on VC1. But I noticed it doesn't run the IBAction mapped to the button. I'm guess this is because it's linked to exit.
So my question is: if I have a button that exits my view controller, can I also have it linked to an IBAction so that I can run some logic on the view controller being exited (VC2) before it exists?
If your button is linked to a segue on the storyboard, its IBActions does not seem to run. I don't know if this is documented anywhere, but that seems to be the behaviour.
Instead of linking the button to the "Exit" of VC1, link your VC2 to the "Exit", then give that unwind segue an identifier, let's say unwindToVC1.
Now you can add your IBAction to the button, and write:
#IBAction func buttonPress() {
// process your data or whatever
// perform the unwind segue
performSegue(withIdentifier: "unwindToVC1", sender: yourData)
}

How to perform conditional segue

I've created a segue through IB. When buttonA is clicked, a transition to viewB will occur.
In the button click action, I have performSegue(withIdentifier:sender:) wrapped in a condition. I want the segue to occur only if the condition is true.
However, once the user clicks the button, the segue happens before the condition is even run. What is the best way to transition programmatically while still using segue identifiers defined in IB?
You have created segue from the UIButton to a viewController that is the reason segue happens before the execution of condition. Instead of that remove that segue from storyboard and create another from your ViewController to DestinationViewController.

Swift popToRoot not working

This highlighted line is where should popToRoot proceed, after a successful registration it should redirect to Root View Controller. For some reason it's not working for me, literally nothing happens, not even error.
I tried with
self.navigationController?.popToRootViewControllerAnimated(true)
You don't appear to be using navigation controller at all, so I'd wager that self.navigationController is nil.
You could use an unwind segue. So in your root view controller, add a method like so:
#IBAction func unwindToRoot(segue: UIStoryboardSegue) {
print("successfully unwound")
}
Then in your scoreboard scene from which you want to unwind, you can control-drag from the button to the "exit outlet":
When you let go, you can pick the unwind action:
This achieves the "pop to root" sort of functionality, but is not contingent upon using navigation controller.
If you want to perform this unwind programmatically, rather than doing the segue from the button to the exit outlet, do it from the view controller icon to the exit outlet:
Then, select the segue in the document outline and give this segue a unique storyboard id:
Then you can programmatically perform the segue, using the same identifier string:
performSegueWithIdentifier("UnwindToRoot", sender: self)

Why is a storyboard popover segue appearing on a subsequent view within a navigation controller

this is odd, but I'll try to explain the best I can.
I have a navigation controller which has a view. The view asks a simple question. That view then has two segues available -- one on the view for a correct answer, which is a "show" on the navigation controller and the other for an incorrect answer which is a "popover".
The segues are tied to the view and the answer box for the show and popover respectively.
I'm testing the answer on button press and using performSegueWithIdentifier to then show the appropriate window.
if (answerField.text == "2") {
println("Correct")
performSegueWithIdentifier("Correct", sender: sender)
} else {
println("Incorrect")
performSegueWithIdentifier("Error", sender: sender)
}
The problem I'm having is that if I get the answer correct, it moves fine to the next view, but it shows the "incorrect" popover view after segueing to the correct one.
I know that sounds complicated, but it's a super simple app at present. I suspect I'm just doing it wrong. Of note is that I also get an error "Presenting view controllers on detached view controllers is discouraged" which may be related.
Many thanks for any assistance anyone can provide.
D.
It sounds like you have one or both of the segues hooked up directly to the button in your storyboard, which will trigger the segue automatically on the button press even when you don't call performSegueWithIdentifier(_:) in code. You are then setting up an IBAction method which is also called on the button press, where you are then programmatically performing the segue again.
You can check this by going to your storyboard and selecting the button. Go to the connections inspector on the right, and I expect you'll see a Triggered Segues section, with an "action" triggering your "show" segue. You can remove this by clicking the cross next to the connection.
For segues that should be initiated programmatically, you should create a segue by dragging from the view controller object (rather from a control / actionable element) to the next view controller. This will create a generic segue that is only triggered by performSegueWithIdentifier(_:) in code.
As you have noticed, since your segue is for a popover presentation it will complain unless it is explicitly anchored to a particular view in your storyboard. You can still drag the segue from the view controller object to the popover view controller, but you must manually hook up the anchor: select the segue in the storyboard, choose the attributes inspector on the right, and drag from the circle in the "Anchor" field to the text field you want to anchor to.

Storyboard: Condition segue transition for navigation (push)

I am new to storyboard and xcode (using 4.6)
I m using Navigation Storyboard with push segue.
I would like to implement a select snap view, that if have minimum 1 snap selected - the segue would work. Otherwise, the user should stay on the same view.
I created 2 UIView: SelectSnapsViewController and ShippingDetailViewController.
In SelectSnapsViewController.m I added the following:
- (IBAction)nextButtonPressed:(id)sender
{
looseDataAlert = NO;
[okkkBtn setFrame:CGRectMake(116, 272, 72, 37)];
if ([appDelegate.selImageDetails count] == 0)
{
label1.text = #"To proceed you need to add snaps.";
[self openAlertView];
}
else{
[self performSegueWithIdentifier:#"MySegue" sender:self];
}
}
When I debug the code - I see that the it always fall in else case of condition, however the segue (transition to the new view) still happens.
How can I prevent the user to be transferred to the new view?
There are two ways to implement conditional segues.
1) Configure the Segues as 'manual' from the ViewController and invoke performSegueWithIdentifier conditionally within your IBAction method that you wire up to event handling for your button.
OR 2) Configure the Segue as a 'Triggered Segue' from your button and implement - (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender in your view controller to perform the conditional logic.
Are you saying that you only want to perform the segue if a condition is true?
If so, instead of creating the segue directly from a control or table cell, create a triggerless segue. A triggerless segue has the view controller as its source, and it won't ever fire automatically. Instead you can fire it programmatically any time you like, including from an IBAction.
To create a triggerless segue, start control+dragging the segue from the containing view controller icon in the scene dock at the bottom of the scene. Drag to the destination scene like normal, and pick the segue type. Select the segue, and in the inspector, choose a segue identifier.
At runtime, when you want to perform the segue, invoke -[UIViewController performSegueWithIdentifier:sender:]. You can pass any object you'd like for the sender, including nil. If the sender has no use to you, pass nil.
So, in summary:
Create a triggerless segue from the view controller to the destination scene
Set an identifier for the segue in the inspector
At runtime, and form code, call -[UIViewController performSegueWithIdentifier:sender:] when you want to trigger the segue.