I have 2 views, TableController and WirelessController. While in TableController I need to pop the WirelessController view. This is what I've tried and nothing happens, no console output either.
WirelessController *wCon = [[WirelessController alloc] init];
[[wCon navigationController] popViewControllerAnimated:YES];
and this has the same problem.
[self navigationController] popViewControllerAnimated:YES];
Is it the fact that I'm using the UINavigationController when it's a view based app?
I think you mean to push, not pop...
WirelessController *wCon = [[WirelessController alloc] init];
[[self navigationController] pushViewControllerAnimated:YES];
Push adds a new item to the top of the stack; pop removes the top item from the stack.
Update
From your comments it seems that...
your first view is an instance of WirelessController.
from there you present a TableController modally
now you want to return to you wirelessController.
In this case you need to send a message back to the presenting view controller (wirelessController) asking it to dismiss the view controller it has presented (tableController)
In tableController:
[self presentingViewController] dismissViewControllerAnimated:YES
completion:nil]];
Whatever is going on, you certainly don't want to do this:
WirelessController *wCon = [[WirelessController alloc] init];
This line will create a new object. You want to return to an existing object.
Pushing and popping viewControllers is an activity generally associated with navigation controllers, which keep an array of managed viewControllers. In that case you would push to add a new controller to the top of the stack, and pop to remove it from the stack. In the absence of a navigation controller, there is no such stack, so push and pop make no sense.
Related
I have an app where my main view is embedded in a navigation controller. From there, buttons push onto other view controllers. This all works fine. However, one of the view controllers it pushes to updates one of the root's values and presents it again. However this time, it only presents the ViewController without the navigation controller, and of course, pressing the button to go back will end in a crash. Hopefully this picture will help understand my issue. The pressing enter thing isn't really a big deal, I just call this function on return of the keyboard.
Code to go back to the main controller:
-(void)createNewMain:(NSString*)newAddress {
ViewController* newController = [self.storyboard instantiateViewControllerWithIdentifier:#"MainView"];
newController.labelText = newAddress;
newController.connected = self.connected;
[self presentViewController:newController animated:YES completion:nil];
}
The problem is simple, you're presenting the instantiated view controller modally.
Replace
[self presentViewController:newController animated:YES completion:nil];
with
[self.navigationController pushViewController:newController animated:yes];
Also, you can make a segue to do that from the storyboard. When the segue executes, it will create a new instance and will not use the previously created one.
Note: If you really don't need to create a new instance, consider using delegation to exchange information between objects.
You don't really want to "return" to a new instance of a root controller. What you need to do to properly return to the root controller is to pop all the other ones from the navigation controller's stack like this:
[self.navigationController popToRootViewControllerAnimated:YES];
Use Delegation to pass the expected/required message you want from your Pi Controller to your Root View Controller and set it up according to the message. You don't need to create a new instance of your Root View Controller from there. You can always go back to your root view controller from anywhere in the navigation stack by using
[[self navigationController] popToRootViewControllerAnimated:YES];
First off, I am aware of this question being asked in a forward manner, but in this case I am asking for a backwards manner in which the Navigation Controller is already designed. With that being said...
I have a UINavigationController with three views: Table, Get, and Avail in that order that was created in IB.
When going forward, I want to go from Table to Get to Avail, but when I hit the "Back" button on Avail I want to skip over Get and go directly back to Table. Is this possible? If so, how?
Here's how I did it:
NSArray *VCs = [self.navigationController viewControllers];
[self.navigationController popToViewController:[VCs objectAtIndex:([VCs count] - 2)] animated:YES];
To be able to override the nav controllers's back button you're going to have to subclass UINavigationController. Check out how in this tutorial: http://www.hanspinckaers.com/custom-action-on-back-button-uinavigationcontroller
Implement the navigation controller's delegate method:
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
It will fire for all VCs in the nav controller stack. (put this code in the VC immediately after your navigation controller, and put < UINavigationControllerDelegate > in the .h)
What you want to do is replace the entire Navigation Controller's stack without the view controller you want to remove in it. Since it's a non-mutable array, you have to convert it to a mutable array before removing the VC, remove the VC, then replace the navigation controller's stack with the call [self.navigationController setViewControllers:newVCs animated:NO];. That's the crucial part. You are replacing the stack after you have loaded the page you are on but since you're keeping the VC you're on, it's still the top item on the stack so there is no visible impact to the user. As long as you don't have a lot of VCs on the stack, it's not an expensive call.
Here's how I did it in the delegate method:
//Remove list setup page if new list was created
if ([self.navigationController topViewController].class == [ItemViewController class])
{
NSArray *VCs = [self.navigationController viewControllers];
if(((UITableViewController*)[VCs objectAtIndex:[VCs count]-2]).class == [NewCardTypeController class])
{
NewCardTypeController *removedObject = [VCs objectAtIndex:[VCs count]-2];
if(removedObject != nil)
{
NSMutableArray *newArray = [NSMutableArray arrayWithArray:VCs];
[newArray removeObject:removedObject];
NSArray *newVCs = [NSArray arrayWithArray:newArray];
[self.navigationController setViewControllers:newVCs animated:NO];
}
}
}
Take a look at UINavigationController's -popToViewController:animated: and -popToRootViewControllerAnimated:, which do exactly what you're asking for. That is, they pop the navigation stack back to a particular view controller, or to the root view controller. You'll still need to intercept the nav controller's back button action to use them, though.
I hope an easy question (but I can't solve it :)) I have 2 views, On view 1 I have UILabels that I change the text on with a button click, if I navigate to view 2 and then go back to view 1 the text is reset.
Is there any way to retain the text on the label when the view is changed?
Originally I used:
-(IBAction) napButton:
(id) sender{ nap *nap1 = [[nap alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:nap1 animated:YES];
this performed a simple view switch. With the suggestion below I changed this to:
-(IBAction) napButton:
(id) sender{ nap *nap1 = [[nap alloc] initWithNibName:#"nap" bundle:nil];
[self.navigationController pushViewController:nap1];
[nap1 release]; } }
The button performs no action and I get warning UINavigationController may not respond to -pushViewController
use UINavigationController. In a memory constrained device this is the best way to push or pop new views while preserving data or state in previous view.
An instance of UINavigationController can be created either in code or in an XIB file with relative ease. It’s thought of as a stack: it has a root view controller, and then new view controllers can be pushed onto the stack (often when the user taps on a row in a table) or popped off the stack (often by pressing the back button).
There are 4our methods are used to navigate user through the stack:
– pushViewController:animated:
– popViewControllerAnimated:
– popToRootViewControllerAnimated:
– popToViewController:animated:
For example if you want to move from view1 to view2, you need to do this from view1 -
SecondViewController *secondView = [[SecondViewController alloc]
initWithNibName:#"SecondViewController" bundle:nil];
[self.navigationController pushViewController:secondView];
[secondView release];
to move back to the previous state, i.e. from view2 to view1, you need to do this from view2 -
[self.navigationController popViewControllerAnimated:YES];
This should solve your problem in the most efficient way.
I have an aplication which initially there's a TabBarController, each tab is a ViewController and every one has a button which calls other controllers.
So how am I supose to structure this? Having one main rootviewController (if so, how?)? Or calling in the appdelegate only the tabBarController and in each the viewControllers inside the tab call the other controllers?
What's the best way so I can advance, go back and transition views nimbly?
Don't know if I made myself clear...
Thanks guys.
Generally you will start with the Template called "Tab Bar Application" and as of Xcode 4 starts by loading the MainWindow Nib, which hold a tab bar and the tab bar is set up in IB to have 2 view controllers, called "FirstViewController", and "SecondViewController"...
You can follow that pattern if it suites you, otherwise you may want to start with a view based application and add your own tab bar. I personally find it to be easier to control the tab bar, through the UITabBarDelegate, especially if you plan to do anything slightly esoteric.
Edit:
Basically one of two ways, if you plan to load a Navigation controller stack, or a single modal view.
1)
ThirdViewController * controller = [[ThirdViewController alloc] initWithNibName:#"ThirdViewController" bundle:nil];
UINavigationController * myNavigationController = [[UINavigationController alloc] initWithRootViewController:controller];
[self presentModalViewController:myNavigationController animated:YES];
[controller release];
[myNavigationController release];
2)
ThirdViewController * controller = [[ThirdViewController alloc] initWithNibName:#"ThirdViewController" bundle:nil];
[self presentModalViewController:controller animated:YES];
[controller release];
either way get back to the Tab environment by calling the following on the view controller that is calling present modal.
[self
dismissModalViewControllerAnimated:YES];
I'm having a problem with an iPhone App using UINavigationController. When I'm using pushNavigationController, it works fine. The iPhone does its animation while switching to the next ViewController. But when using an array of ViewControllers and the setViewControllers method, it has a glitch in the animation which can grow into a clearly visible animation bug.
The following snippet is called in the root ViewController. Depending on a condition it should either switch to ViewController1, or it should directly go to ViewController2. In the latter case the user can navigate back to vc1, then to the root.
NSMutableArray* viewControllers = [NSMutableArray arrayWithCapacity:2];
// put us on the stack
[viewControllers addObject:self];
// add first VC
AuthentificationViewController* authentificationViewController =
[[[AuthentificationViewController alloc] initWithNibName:#"AuthentificationViewController" bundle:nil] autorelease];
[viewControllers addObject:authentificationViewController];
if (someCondition == YES)
{
UserAssignmentsListViewController* userAssignmentsListViewController =
[[[UserAssignmentsListViewController alloc] initWithNibName:#"UserAssignmentsOverviewViewController" bundle:nil] autorelease];
[viewControllers addObject:userAssignmentsListViewController];
}
[self.navigationController
setViewControllers:[NSArray arrayWithArray:viewControllers] animated:YES];
As you can see I'll add the first and maybe the second VC to the array, finally setting the navigationController stack with animation. This works properly if I only add the first controller. But in the case where the animation should go to the 2nd controller, the navigation bar's title won't be "flying in". Instead there is an empty title until the animation is finished. And, even worse, if I replace the navbar title with a custom button, this button will be displayed in the upper left corner until the animation is finished. That's quite a displaying bug.
I tried to use a workaround with multiple pushViewController methods, but the animation doesn't look / feel right. I want the navigation to do its animation in the same way as pushViewController does. The only difference here is, that I don't add a VC but set the whole stack at once. Is there another workaround here, or could this be considered as a framework's bug? I thought about using only pushNavController for VC2, then somehow insert VC1 into the stack, but that doesn't seem possible.
Thanks for all hints and advices. :-)
Technical data: I'm using iOS 4.2, compiling for 4.0.
Finally I found the solution. The mistake was that the new top-level NavigationController has not been initialized and loaded properly until the animation is done. In my case, UserAssignmentsListViewController has a viewDidLoad method that will not be called until animation is done, but it sets the navigation title (here: a UIButton). Therefore the animation fails.
The solution is to refer to an already initialized view controller when it comes to pushing it to the stack. So initialize our top-level VC somewhere:
// initialize our top-level controller
ViewController* viewController2 = [[[ViewController alloc]
initWithNibName:#"ViewController" bundle:nil] autorelease];
Then when pushing two or more VCs to the stack, the top level one is already initialized and the animation works (following the example from my original question):
NSMutableArray* viewControllers = [NSMutableArray arrayWithCapacity:2];
// put us on the stack, too
[viewControllers addObject:self];
ViewController* viewController1 = [[[ViewController alloc]
initWithNibName:#"ViewController" bundle:nil] autorelease];
[viewControllers addObject:viewController1];
if (someCondition == YES)
{
[viewControllers addObject:viewController2];
}
[self.navigationController
setViewControllers:[NSArray arrayWithArray:viewControllers] animated:YES];