My hierarchy
UINavigationController -> UIViewController
-> UITabViewController
-> ViewController1
-> ViewController2
-> ViewController3
I want to navigate back from
ViewController1 -> UIViewController.
Anyone know please solve this issues.
"Unwind" is your answer.
Create an IBAction method to unwind segue. Define this method in a controller in which you want to unwind (Controller from you want to jump back to main controller).
- (IBAction) prepareForUnwind:(UIStoryboardSegue *)segue {
}
Now connect back button (in this case "Home" button) with this method in Storyboard. To connect unwind action -> Ctrl-drag to "Exit" outlet button of your controller.
Note: If you are using Xcode version less than 6.0 than "Exit" outlet is located at bottom of your view controller.
This will navigate back to your root navigation controller. That is UIViewController.
For further separation you can give an identifier to unwind segue and make different actions for exit to last controller.
Select identifier from left list and give an identifier in Attributes inspector.
Key points:
Write unwind method in a controller which will be exited.
Connection to "Exit" delegate will only works after you define unwind method.
Ctrl+Drag from a control to the Exit symbol to select the unwind segue you want this control to perform
Unwind segues appear below the Exit symbol for each connection made
You can give those unwind segues an identifier to have different activities performed.
You can use
[navigationController popToViewController:<#(UIViewController *)#> animated:<#(BOOL)#>];
just provide the instance of the viewController you want to pop to and set animated property to YES if u want pop with animation and NO if not.
Here the controller UIViewController is the rootViewController of UINavigationController. So you just need to pop to the rootViewController from the ViewController1
[self.navigationController popToRootViewControllerAnimated:YES];
You can done by this:
ViewController *loginVC = [self.storyboard instantiateViewControllerWithIdentifier:#"ViewController"];
[self.navigationController popToViewController: loginVC animated:YES]
Related
I have 3 MVCs and name them A, B, C here.
A is the main MVC with a button "Menu" connected to B with popover segue.
A also connected to C with a manual show segue.
B is a popover MVC with a button "Detail" connected to A with unwind segue.
C is the detail MVC with detail info.
Inside the unwind function of A. I call performSegueWithIdentifier to show C.
Expected behavior is
Click "Detail" button in B
B disappear and A show up
C show up
But running the app I got.
Click "Detail" button in B
B disappear and A show up
C show up
C disappear and A show up
C show up and disappear suddenly which is not what I want.
Some additional info
Popover B is needed for more buttons.
A is embeded in a UINavigationController. Connecting A -> C rather than B -> C, for a Back button on top of C.
Seems unwind function is not the correct place to call performSegueWithIdentifier.
UIKit will pop view controller after calling the unwind function.
Thus push new view controller inside unwind function will pop quickly.
The solution is to delay performSegueWithIdentifier.
Solution 1: NOT WORKING FINE
Adding an bool instance and inside viewDidAppear use this instance to determine if we perform segue.
Won't work if B controller is a popover. After B popover disappear, viewDidAppear is not called for A controller.
Solution 2: WORKING
Push performSegueWithIdentifier into main queue.
#IBAction func unwind(segue : UIStoryboardSegue) {
dispatch_async(dispatch_get_main_queue()) {
self.performSegueWithIdentifier("SomeSegue", sender: self)
}
}
you can easily switch between the viewControllers by assigning storyboard id to them using below code ..
// you need to create UIStoryboard object by giving name of your storyboard
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
// here you need to create storyboard ID of perticular view where you need to navigate your app
UIViewController *vc = [mainStoryboard instantiateViewControllerWithIdentifier:#"viewContIdentifire"];
// if use presentViewController this will not enables you to go back to previous view
[self presentViewController:vc animated:NO completion:nil];
**// OR**
// using pushViewController lets you to go back to the previous view
[self.navigationController pushViewController:vc animated:YES];
Or still you want to work with segue so must gain control over them by implementing its delegate method below so they perform accordingly.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"segueName"]) {
// perform segue
[self performSegueWithIdentifier:#"SegueIdentifier" sender:self];
}
}
happy coding .. :)
I have 3 viewControllers. viewController1 is linked to viewController2 (via segue id "first"), and viewController2 is linked to viewController3 (via segue id "destinationController"). I'm trying to segue from viewController1 to viewController3. Here is my code:
UIViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"DestinationController"];
[self.navigationController pushViewController:controller animated:YES];
It crashed with the following error:
'Storyboard (<UIStoryboard: 0x7fb1a944be00>) doesn't contain a view controller with identifier 'DestinationController''
How can I segue 2 viewControllers without connecting the first to the last? (I prefer not adding a segue from the storyboard because it will get very messy.)
The error is telling you that you don't have a controller with the identifier, "DestinationController". From the text in your question, it seems that you have a segue identifier called "DestinationController", but that's a different thing from an identifier on the controller, which you set in the Identity Inspector with the "Storyboard ID" field.
I think it would be more consistent though, to make a segue from the first to the third view controller. It also makes the navigation among your controllers clearer in the storyboard.
Please check "DestinationController" segue is bind with you current ViewController in which you write this code.
If you perform segue from VC1 to VC3 there is only one way in your case you have to go through VC2 (You can place conditions on viewDidLoad method of VC2 to perform segue with VC3).
Please bind "DestinationController" to your current controller and your error will gone.
This may help you :)
UIViewController3 *controller3 = [self.storyboard instantiateViewControllerWithIdentifier:#"DestinationController"];
[self.navigationController pushViewController: controller3 animated:YES];
Don't forget the set the storyboardId "DestinationController" not the segue name, Check the below Link,
Check the answer
I am trying to make a login in screen where when you press login it will check the username and password then if it is all good it will segue to the next view.
I have 1 view controller with 2 buttons just to test that I can successfully segue with a button action to another view controller. 1 button is my attempt to programmatically link the two view controllers and the other button is linked by a modal and it successfully switches between the views.
for the first button my code is,
#IBAction func testButton(sender: UIButton) {
performSegueWithIdentifier("nextView", sender: self)
}
for the storyboardID of the view controller to be switched to it is indeed 'nextView'
I get an error, 'NSInvalidArgumentException', reason: 'Receiver has no segue with identifier 'nextView'
I have cleaned up the code and also tried removing the app from the simulator.
Any suggestion on what the issue may be? Or is there a better way to make a login screen?
In order to perform segue on button tap with custom logic, select controller one (not button) control drag and drop on controller two. chose the segue and give an identifier value in storyboard. Then when you call performSegueWithIdentifier, it will work.
You need to set the identifier for the segue, not the StoryboardID of the view controller:
try this UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainstoryBoard" bundle:nil];
ViewController* viewController = [storyboard instantiateViewControllerWithIdentifier:#"StoryBoardIdentifier"];
[[self navigationController] pushViewController:viewController animated:YES];
performSegueWithIdentifier refers to the id of the segue not the storyboardID of the viewController you're trying to load. That being said, you could either create a segue between the view controller you're on to the destination view controller and use performSegueWithIdentifier, or you could create an instance of your storyboard and use its instantiateViewControllerWithIdentifier method which takes the storyboardID as a parameter. I'm posting from mobile so that might not be exact, but that should help you out.
I have used a storyboard and dragged the navigations between controllers . E.g From UIViewController A to UIViewController B to UIViewController C.
I have a button on UIViewController A. I am using that button and linking an action dragging to UIViewController B and setting the segue to push type.
Then on the button Click,I am checking some condition like this :
if( go == YES )
{
// navigate to B
}
else
{
// don't navigate to B
}
How can I write the code to properly navigate to B or stop navigating to B?
Do I have to use prepareForSegue?
Till here, you have done it correctly. Now try these steps :
The segue that you have connected by dragging from A to B, set the segueIdentifier for it in the Interface Builder.
In your button click method, use this statement when you want to navigate from A to B.
[self performSegueWithIdentifier:#"yourSegueIdentifier" sender:nil];
Here you will have to provide the identifier name that you set in the Interface builder.
Your problem is that you have directly connected the button to push Segue to the B Controller. To stop navigating to B when condition is false, follow these steps :
Delete the current Segue from A to B.
Now Connect a segue from the ViewController button of the View in your storyboard to the B Controller. Look out for this yellow button below your A ViewController. See that yellow button at the bottom image. Connect a segue by dragging from that button to your B ViewController.
Now name that Segue with the name you gave before i.e loginSegue and try running your project. Now it won't go to B if condition is false.
Hope this helps.
No need to use prepareForSegue every time.
You can also push view controller by this code.
if( go == YES )
{
// navigate to B
UIStoryboard *story = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
viewController *viewObj = [story instantiateViewControllerWithIdentifier:#"viewController"];
[self.navigationController pushViewController:viewObj animated:YES];
}
else
{
// don't navigate to B
}
For this you have to give storyboardID to viewcontroller as "viewController" in your storyboard.
I have a problem,
The following is my StoryBoard,
the first Controller is a TabBarController,
and it relation with A (ViewController).
A is a ViewController,
B is a NavigationController, A change page to B by modal segue
C is a ViewController, C will change to another page by push so I need a NavigationController
OK, I want to pass value from A to C,
now I can pass value from A to B by prepareForSegue,
However, because B and C have relationship but not segue,
So I can't pass value from B to C by prepareForSegue!!!
How can I pass value between NavigationController and ViewController with StoryBoard?
The Storyboard image is a little misleading here.
When you segue to B, actually you are segueing to the B/C combo as NavControllers always have at least one viewController in their stack (which is their topViewController and their [viewControllers objectAtIndex:0]).
So you do have a relationship directly from A to C.
How you access that controller depends on whether your segue is modal or push. In your case it is modal, but I will describe both so you can see the difference.
In either case, to pass data to C, you need to declare a property in it's header file
#interface CviewController: UIViewContrller
#property (assign) int dataFromA;
#end
push segue
In a push segue, it is actually C that is the destinationViewController, not B. In fact the push segue is governed by B, which is the UINavigationController for both A and C. The code behind the push segue is of the form
[self.navigationController pushViewController:otherViewController];
In AviewController's prepareForSegue:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
CviewController* controller = segue.destinationViewController;
[controller setDataFromA:self.data];
}
It is possible in the storyboard to make a push segue line between two viewControllers that do not share a common UINavigationController. However when you run this you will get a crash error:
'Could not find a navigation controller for segue 'pushC'. Push segues can only be used when the source controller is managed by an instance of UINavigationController.'
Behind every good push segue lies a Navigation Controller.
modal segue
The code hiding behind a modal Segue is the UIViewController method
- (void)presentViewController:(UIViewController *)viewControllerToPresent
In a modal segue to a NavController/ViewController combo, the destination viewController is whatever the segue line points to. If it points to a viewController, that is the segue.destinationController (and the UINavigationController will be ignored, which is not what you want here); if it points to a UINavigationController, as in this case, that will be it's destinationController. But it is still straightforward to access the viewController, as it will be the navigation Controller's topViewController.
In AviewController's prepareForSegue:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
CviewController* controller =
(CviewController*)[[segue destinationViewController] topViewController];
[controller setDataFromA:self.data];
}
Note that in this case we have to use old-style [[message passing] syntax]. If we use modern.property.syntax we get a compile error. That's because the program does not know the type of desinationViewController, and refuses to accept topViewController as a property of an unknown type. But it is happy to [send [real messages]] to an unknown type. We also have to (typecast*) to avoid compiler warnings.