Let's say I have 3 controllers (A, B, C). A and C is ViewControllers, B is NavigationController. Normal application flow is A as root view, A present (modal) B, B push C.
What I want is to present C as top view controllers without going through all the animation from A-B-C but still have the hierarchy (means C can go back to A), is it possible?
We can set window rooViewController directly to C but it wont have the hierarchy
EDIT:
Maybe my question isnt clear enough, the main point here is, when I open my app, I want to show C directly but still have A->B->C view hierarchy so I can go back to A via normal pop and dismiss
EDIT2:
I manage to show C with B-C hierarchy, so I can pop back to B from C. Now my problem is how can I present B (NavigationController) from A (ViewController) so when I close B it will **dismiss* to A
EDIT3:
I saw some answer that use NavigationController, it works BUT not what I want because normally from A to B I use modal presentViewController and from B to A I use dismissViewController
EDIT4:
So far what I got is
self.window.rootViewController = vcA;
[self.window makeKeyAndVisible];
[vcA presentViewController:vcB animated:NO completion:nil];
[vcB pushViewController:vcC animated:NO];
this will give correct hierarchy that I want but it give fast animation (a blink) showing A and than C and also give warning Unbalanced calls to begin/end appearance transitions for <vcA: 0x7fcfa0cf9c50>.
EDIT5:
I endup ignoring the warning and stick with my prev answer (but still welcome for another solution). And for the blinking problem I use workaround below
uiview *overlay = [new uiview]; // using vcA.frame
overlay.backgroundColor = white; // I use dominant color of vcC
vcA addSubview:overlay;
self.window.rootViewController = vcA;
[self.window makeKeyAndVisible];
[vcA presentViewController:vcB animated:NO completion:^{
[overlay removeFromSuperview];
}];
[vcB pushViewController:vcC animated:NO];
This will disguise the blinking behavior so no one will notice (I hope :-p)
Use a UINavigationViewController and then call
setViewControllers(_:animated:)
Use this method to update or replace the current view controller stack without pushing or popping each controller explicitly. In
addition, this method lets you update the set of controllers without
animating the changes, which might be appropriate at launch time when
you want to return the navigation controller to a previous state.
If animations are enabled, this method decides which type of
transition to perform based on whether the last item in the items
array is already in the navigation stack. If the view controller is
currently in the stack, but is not the topmost item, this method uses
a pop transition; if it is the topmost item, no transition is
performed. If the view controller is not on the stack, this method
uses a push transition. Only one transition is performed, but when
that transition finishes, the entire contents of the stack are
replaced with the new view controllers. For example, if controllers A,
B, and C are on the stack and you set controllers D, A, and B, this
method uses a pop transition and the resulting stack contains the
controllers D, A, and B.
Let me know if it help you :)
push A and B and C like you normally would but do it by using presentViewController:? animated:NO and pushViewController:? animated:NO -- not animating is the clue
e.g. (mock code)
applicationDidFinishLaunching {
id a = [MyA new]; //root, presents b
id b = [MyA new]; //pushes c you said.. so it is or has a navigationController
id c = [MyA new];
[a presentViewController:b animated:NO];
b.navigationController pushViewController:c animated:NO];
}
To segue to any ViewController you want to: Have you drawn a custom segue in the storyboard by holding down the control key on your keyboard and clicking the ViewController you want the segue to be in? While still holding control, you can drag it to the viewcontroller you want to segue to. After that just let go and XCode will let you choose the type of segue you want: push, modal, or custom.
After that, click the visual segue reference that Xcode creates (looks like a big grey arrow in the storyboard that points to your viewcontrolelrs) and click the attributes inspector. Then look where it says identifier. From there you can name the segue anything you want and reference it programmatically. Just do the above and call the below message and you should be able to go to any viewcontroller when ever you want to.
[self performSegueWithIdentifier:#"ShowViewControllerA" sender:self];
Also, I agree with everyone else saying to set Viewcontroller C as root ViewController in the storyboard. PresentViewController is also a good idea. etc
Follow #Daij-Djan's answer:
applicationDidFinishLaunching {
id a = [MyA new]; //root, presents b
id b = [MyA new]; //pushes c you said.. so it is or has a navigationController
nav d = [[Nav alloc] initWithRoot:b];
id c = [MyA new];
[a presentViewController:d animated:NO];
b.navigationController pushViewController:c animated:NO];
}
Why don't you just present C on top of A with presentViewController?
EDIT:
A -> C:
In vcB I would add a boolean property indicating whether we are in the mentioned flow and present vbB in vcA this way:
// We are in vcA where you want to present vcB and vcC
vcB.transient = YES;
[vcA presentViewController:vcB animated:NO completion:^{
[vcB pushViewController:vcC animated:NO];
}];
C -> A
When you want to go back to vcA, popping vcC will call viewDidAppear in vcB.
// We are in vcB
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if(self.transient == YES) {
[self dismissViewControllerAnimated:NO completion:nil];
return;
}
}
With this solution when you go back from vcC to vcA you will temporary see vcB, as we have to wait for viewDidAppear to be called.
EDIT 2:
Alternatively if you don't need to directly go back from vcC to vbA, just use the first piece of code (no transient property required).
Keep A as rootVC(in applicationDidFinishLaunching). So when you open your app it will load A first.
Once A is loaded(in viewDidLoad) call a method to present B(keep animation = NO while presenting).
When B is loaded, call a method to push to C(keep animation = NO while pushing).
Hope this helps!
I'm quite new to iOS programming and I'm trying to do something that I think is simple but I can't find clear explanation.
I'm trying to develop an app with Xcode 6.3 and swift 1.2
I have this :
So I'm in a tab based application. Each tab view are inside navigation controller.
D is a modal launch from C. When I click on a button inside D I want to go to B. This is fine. But I would like also to display the top navigation bar with a back button pointing to A.
For now when I tried to use the push segue from D to B, B is shown as modal but not part of the navigation controller.
How I have to achieve this easily ? Do I have to recreate all the stack by instantiating each view (A then B) and push everything onto the navigation stack ?
If you know the code to achieve this in objective-c it's fine for me as well.
When I click on a button inside D I want to go to B. This is fine. But I would like also to display the top navigation bar with a back button pointing to A.
You can only go back to A, if it is actually on the navigation stack. So, you will have to somehow push it onto the stack before you can achieve your goal.
As a general hint, if you want a view controller to show the navigation bar, you only have to make sure it's embedded into a navigation controller. When you present a view modally, it will not by default be embedded into a navigation controller. So, as you said D is shown modally from C, just embed D into another navigation controller and instead of making D the destination for the modal segue, make the navigation controller the destination.
Update
If you want to show a back button in B that points to A, even though B was shown modally from D, you will have to hack your way around because A is not on the navigation stack. So, logically, what you have to do is make sure that A is on the navigation stack just before B. I don't think you can do this simply with segues from IB, but rather use code and instantiate the UINavigationController yourself:
- (void)showBModallyWithBackButtonToA
{
A *a = [[A alloc] init];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:a];
B *b = [[B alloc] init];
[nav pushViewController:b animated:NO];
[self presentViewController:nav animated:YES completion:nil];
}
This code has to be executed from D, if I understand your setup correctly.
Here is the solution from nburk converted in swift with the use of storyboard :
let a = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("aIdentifier") as! ATableViewController
let nav = UINavigationController(rootViewController: a)
let b = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("bIdentifier") as! BViewController
// b parameters here
// b.parameter1 = variable
nav.pushViewController(b, animated:false)
self.presentViewController(nav, animated:true, completion:nil)
Where aIdentifier and bIdentifier are the identifier you set inside storyboard editor for each view and ATableViewController and BViewController their respective ViewController classes.
I have a instance of UINavigationController namely N declared for my UIPopoverController in a method. I have two UIViewController namely A and B. Initially when I load the popover I assign viewcontroller A to my navigation controller N so A's view get displayed. At this point when N displays A, N has done button of type UIBarbuttonItem assigned as rightNavigation item and calls a method namely M().
So Here is the Question- When I press Done I need to load view controller B in the called Method M(). That is push B in N but for doing that I need the instance of navigation controller N from the UIBarButtonItem that I pressed. I assumed some thing like
-(void)M:(id)sender
{
UINavigationController *N = barButton.parentController;
[N pushViewController:B animated:NO];
}
But I didnt arrive to any solution. Can someone please help me with this. Thank you.
In method B, you use :
[self.navigationController pushViewController:ac animated:YES];
//ac == UIVIewController which you want to push
i transferred from View Controller A to View Controller B with this code
UIViewController *HomePageView = [self.storyboard instantiateViewControllerWithIdentifier:#"HomePageView"];
[self.navigationController setViewControllers:#[HomePageView] animated:YES];
// The above code transfers the user from the A to B after verification.
Now i am trying to connect a view controller B to view controller C with a push segue. The button that does the action is a navigation bar button,but anytime i try to click on the button it doesn't respond. I also think I'm have this problem because view controller B is not my initial scene. Any ideas on what i can do?
Please view picture link to have an idea. http://i.stack.imgur.com/ijWjl.png
Try using:
[self presentViewController:HomePageView animated:YES completion:nil];
in place of:
[self.navigationController setViewControllers:#[HomePageView] animated:YES];
(if you don't want to go back from B to A).
I have hierarchy of ViewControllers in my storyboard structure.
It is A-B-C-D. A is embed with NavigationController and the flow goes on till D viewController. All fours view attached through segues. Now I am on D viewController, I defined some action to the button of D that It should take me directly to A viewController that is rootViewController or B viewController. Then how can I achieve this. I tried everything but didn't succeed.
I want something like it should not disturb A-B-C-D flow and it should take me to A viewController from D.
Right click on your D viewcontroller and drag i to your A viewcontroller.
Then click on the object which appears on the line you just created.
Write something like DtoA in the storyboard segue identifier in the attributes inspector.
Now in D view controller, just do:
[self performSegueWithIdentifier:#"DtoA" sender:self];
And if you instead wish to pop to a previous viewcontroller the old fashioned way, like from D to B:
UINavigationController* navController = self.navigationController;
UIViewController* Bviewcontroller = [navController.viewControllers objectAtIndex:1];
[navController popToViewController:controller animated:YES];
I hope this helps!