iPhone UINavigation Issue - nested push animation can result in corrupted navigation bar
Asked Answered
P

20

68

I keep getting the following errors:

2011-04-02 14:55:23.350 AppName[42430:207] nested push animation can result in corrupted navigation bar
2011-04-02 14:55:23.352 AppName[42430:207] nested push animation can result in corrupted navigation bar
2011-04-02 14:55:23.729 AppName[42430:207] Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted.
2011-04-02 14:55:23.729 AppName[42430:207] Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted.

Here is what I am doing. From a view controller, I call the following when a certain button is pushed:

EventsViewController *viewController = [[EventsViewController alloc] init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:viewController];
navController.navigationBar.tintColor = [UIColor blackColor];
[self presentModalViewController:navController animated:YES];
[viewController release];
[navController release];

Then, if a certain button is pushed in EventsController, I call:

SingleEventViewController *viewController = [[SingleEventViewController alloc] initWithEvent:[currentEvents objectAtIndex:indexPath.row]];
[self.navigationController pushViewController:viewController animated:YES];
[viewController release];

Then, if a certain button is pushed in SingleEventViewController, I call:

EventMapView* viewController = [[EventMapView alloc] initWithCoordinates];
[[self navigationController] pushViewController:viewController animated:YES];
[viewController release];

So yea, it's obvious that there's nested push animations, but isn't this the right way to go about it? I checked out Apple's DrillDownSave code and this appears to be how they're doing it. Does it matter that I use init methods instead of viewDidLoad methods?

Piton answered 2/4, 2011 at 20:0 Comment(0)
P
9

I've figured it out. Apparently if you call -pushViewController from outside of the -didSelectRowAtIndexPath method of a UITableViewDelegate, it doesn't work. Moving the call into that function worked. Weird.

Piton answered 9/4, 2011 at 19:51 Comment(2)
This solves the issue because didSelectRowAtIndexPath cannot be called until after the view has finished animating. If you simply make sure to call your pushViewController in viewDidAppear: or later, iOS will not complain about nested pushViewController calls.Chanterelle
i dont really understand "Apparently if you call -pushViewController from outside of the -didSelectRowAtIndexPath method of a UITableViewDelegate, it doesn't work.".It works in my project.Khania
L
120

Calling pushViewController before viewDidAppear is unsafe.

Ladysmith answered 13/7, 2011 at 6:54 Comment(5)
Definitely this is the explanation to the problemSeppuku
Also happens if you call pushViewController on secondary thread.Mcgruder
I see how this would be the case. However, can you provide some documentation that talks about this? It's not in the UINavigationController docsLilah
@PietroRea I don't think such a claim can be found verbatim in the doc. However, the doc says pushViewController is "typically used in response to the user selecting an item" i.e. after the current view is available. It is therefore logical to expect that reordering these events can confuse the internal FSM.Ladysmith
Really thank you .You saved my time I am using reveal menu and when I push viewcontroller on viewwillappear , everthing was false. thank you really :)Bergquist
O
91

ACCIDENTLY TRIGGERING THE SAME SEGUE TWICE Once in code, and once from interface builder, but both at the same time...

I was getting the same error as the rest of you. Only my problem was I was accidentally firing the same segue, twice. Once from interface builder, and once from within my code.

I have a UITableView. When a cell is selected, a segue in interface builder fires. Heres my problem, I had the segue set up to be directly fired off clicking the CELL ITSELf, inside interface builder, then in my code, I had under didSelectRowAtIndexPath, code that would fire that same segue... like so...

[self performSegueWithIdentifier:@"MySegue" sender:tableView];

That means when didSelectRowAtIndexPath gets called because a row was selected, it fires the segue with the above line of code. Then interface builder, also triggers the segue, because its connected directly to the cell object in interface builder. To stop interface builder from directly firing the segue. You have to connect the segue from the top of the view controller, not nested down inside coming off of the cell itself.

So if you are having this problem for the same reason as me, that is, you are calling the same segue twice, you can fix this by unlinking the connection from the CELL DIRECTLY, to your segue, and having the segue connection originate at the top of the table hierarchy in IB, rather than nested inside the cell. Connect the segue from you View Controller itself, to the segue. If you have done this correct, when you select the segue, it should highlight the ENTIRE view it is coming from, not just the cell.

Now Apples documentation states thus under the performSegueWithIdentifier:sender: reference:

Apps normally do not need to trigger segues directly. Instead, you configure an object in Interface Builder associated with the view controller, such as a control embedded in its view hierarchy, to trigger the segue. However, you can call this method to trigger a segue programmatically, perhaps in response to some action that cannot be specified in the storyboard resource file. For example, you might call it from a custom action handler used to process shake or accelerometer events.

In my case, I have a search button for my UITableView, and whether the segue is called when the search results table is present, or the normal table view is present, had to be determined. So I needed to trigger the segue directly.

So remove the embedded control from interface builder, and just stick it on the view controller itself, then trigger the segue in your code!

Now, no more double segues! And no more errors.

Otherworld answered 19/1, 2013 at 10:13 Comment(3)
my problem was also causing by twice trigger for a buttonScull
I had setup a segue for a UICellView in IB and then i implemented code to create a new VC through self.storyboard and passing in my data to that VC and then pushing through [self.navigationController pushViewController:vc animated:YES];. My solution was to remove the segue in IB.Windpollinated
I am not using storyboard. I am using pushviewcontroller. How to prevent this problem?Carver
J
13

I had the same problem / error message as you did just now, was looking for a solution and ended up at this thread, however, for me I found that the solution is actually having only one animated:YES when doing a nested push (I put animated:YES only for the final push), hope this helps

cheers.

Jacobba answered 11/4, 2011 at 5:2 Comment(3)
Your comment allowed me to solve the puzzle -- see my comment to the accepted answer below, it may help you in the future as well.Chanterelle
I wasn't getting an error, but my first view's navigation bar was showing the middle view's navigation bar after popping from the third view. Not animating the pop from the top solved the problem.Anderton
Thanks, that makes so much sense when you mention it. I'd pushed the initial controller with animated:YES then animated:NO-ily pushed the rest to get the user to the right spot.Acceptation
P
9

I've figured it out. Apparently if you call -pushViewController from outside of the -didSelectRowAtIndexPath method of a UITableViewDelegate, it doesn't work. Moving the call into that function worked. Weird.

Piton answered 9/4, 2011 at 19:51 Comment(2)
This solves the issue because didSelectRowAtIndexPath cannot be called until after the view has finished animating. If you simply make sure to call your pushViewController in viewDidAppear: or later, iOS will not complain about nested pushViewController calls.Chanterelle
i dont really understand "Apparently if you call -pushViewController from outside of the -didSelectRowAtIndexPath method of a UITableViewDelegate, it doesn't work.".It works in my project.Khania
H
8

I happened upon this same problem that resulted from a button in a nib being connected to two different actions. It tried loading both view controllers, thereby corrupting the stack.

Havens answered 20/2, 2012 at 3:42 Comment(3)
I also had a similar problem. In my case I have a mixture of storyboard segues and programatic segues (performSegueWithIdentifier). I got this error when I accidentally did both at the same time.Gynaecocracy
This was my issue too. I had static table cells that "push"ed to the next controller. I also had a performSegueWithIdentifier in didSelectRowAtIndexPath. both got called resulting in this message.Selfjustifying
Mine was almost exactly the problem as described in this answer, only in IB I somehow had two identical actions for the same button. I'm convinced this came about because I used copy and paste within IB. Be careful.Suggestion
R
4

What do you mean when you say you use init methods instead of viewDidLoad methods?

If you're pushing a new view controller before the old push has bad a chance to be actioned, you will get this sort of error. So putting certain code into init and doing things prematurely could certainly get you the error being reported.

At the point where init is being run on a view controller, the view hasn't been loaded yet!

Reedbuck answered 3/4, 2011 at 0:32 Comment(4)
My only reason for saying I use init over viewDidLoad or loadView is because Apple's example app doesn't use the init methods to set up the views so I thought perhaps that could be part of it. Also, even after I commented out all the code in the init method, I still get this issue. Why wouldn't it be happening for SingleEventViewController as well, though, if that was the case?Piton
You don't use init to set up your UI/view items -- it's not the usual place. Have a close look at UIViewController documentation. A view can be loaded from a nib. Or you can do it programmatically in loadView. Or you can have a basic view set up via a nib, and then add more items programmatically in viewDidLoad. But you shouldn't be doing that sort of stuff in init.Reedbuck
I do it programmatically. So should I be allocing the memory and setting all of the properties in loadView and then actually adding that stuff to the view in the init? Because if I try to to do like [self.view addSubview:myTableView]; in the loadView method, it crashes.Piton
Hell yea, If we do a Perform Segue just by within the ViewDidLoad method, it gives the above errors, and not only that in the next ViewControllers, there contentViews are corrupted, they would show the same older From View Controller contents. So what I did is, I removed the performSegue method call from ViewDid Load and put it onto a Button action and problem resolved. And I had try and wasted that method call in ViewDid Load method, just for testing purpose!Because I needed to do that soon after the View is loaded.So I separated it from the ViewDidLoad.Erased
V
2

Um I had this issue, and Im new to the whole iOS dev scene. But after looking at my connections inspector (with file's owner) in the interface builder i saw that as I had copied a button it had the previous buttons method assigned to it as well as the new method I had created. I guess that was where the nested aspect of my problem came from, as it was executing 2 different methods both of which pushed a view onto the Nav Controller. I know this has already been answered but I figured I would put this up just in case anyone else had a silly mistake like mine.

Vogel answered 2/10, 2013 at 14:57 Comment(0)
V
1

This has already been answered, but I thought this might help others as I got the same error but without using table views. I finally figured out the problem.

I had an existing button whose IBAction invoked a pushViewController. I had created a new button by copying the existing button. The new button also had an action that invoked pushViewController. When the new button was tapped (touch up inside) and the view controller was pushed, I got this error. I deleted the new button, created it from scratch, bound it to the existing outlets and actions, and the error went away.

Verret answered 17/7, 2012 at 0:21 Comment(0)
B
1

Ran into the same problem. In my case I was missing a break in the switch statement so two segues were fired at the same time. Easy fix for me.

Bourn answered 9/6, 2013 at 12:7 Comment(0)
I
1

My problem had to do with the keyboard being active.

This was caused for me by pushing a ViewController from a textField's delegate method:

-(void)textFieldDidBeginEditing:(UITextField *)textField{

        FilterLocationViewController *destViewController = (FilterLocationViewController *)[self.storyboard instantiateViewControllerWithIdentifier:@"FilterLocationViewController"];

        [self.navigationController pushViewController:destViewController animated:YES];

}

By changing the code to this:

-(void)textFieldDidBeginEditing:(UITextField *)textField{

        [_textFieldLocation resignFirstResponder]; //adding this line

        FilterLocationViewController *destViewController = (FilterLocationViewController *)[self.storyboard instantiateViewControllerWithIdentifier:@"FilterLocationViewController"];

        [self.navigationController pushViewController:destViewController animated:YES];

}

(adding the line [textField resignFirstResponder];) the problem went away.

Basically the lesson is that you shouldn't modify the navigationController stack if the keyboard is out.

Idyll answered 2/4, 2014 at 2:49 Comment(0)
O
1

Recently, I've faced the same problem. The reason was: -I was trying to pop view controller twice by mistake. you can check this crash by setting breakpoints on push and pop View controllers

Ovalle answered 31/7, 2014 at 9:6 Comment(0)
V
0

1) Perhaps you could try passing the necessary variables as properties before pushing the UIViewController rather than using the init methods with parameters. Most likely you will need these parameters beyond your init method anyway.

Also, in your initWithCoordinates: method you are missing the parameters. Possibly your custom init methods are a part of the problem.

2) Just because you mentioned viewDidLoad -- this method is for initialization after a view has loaded . If you create the UIViewController in code, as it seems you do, you should use loadView to set up your subviews.

Vermouth answered 3/4, 2011 at 1:55 Comment(1)
The initWithCoordinates method doesn't take parameters yet. I planned on adding them but then ran into this issue first and got caught trying to debug it before moving on. However, why is that not the case with SingleEventViewController, then, too? It takes parameters at the init that it has to set up. And thanks, I'll try with loadView.Piton
F
0

My Solution was

[self performSelector:@selector(moveTo) withObject:nil afterDelay:0.5];

Friarbird answered 3/4, 2013 at 14:39 Comment(0)
S
0

This was happening for me because of my UIControlEvents

    [button addTarget:self action:@selector(callSecondView) forControlEvents:UIControlEventAllTouchEvents];

I had to change the UIControlEventAllTouchEvents to UIControlEventTouchUpInside or however you want your button to work if you had the issue because of a UIButton call.

Statocyst answered 11/7, 2013 at 18:32 Comment(0)
R
0

Don't know about other's. I think most of the People using StoryBoard is facing such Problem. I am using XIB.

In my case The Problem Was, when I was moving to another view using push, I was also using

 [self.navigationController popViewControllerAnimated:YES];

in the ViewWillDisappear of the current View at the same time. Just remove it and it works fine.

I was using POP, because of the requirement and the Flow. The Hierarchy was 1 -> 2 ->3

I was on view 2 and wanted to move to view 3. In that case I encountered this error.

Remains answered 19/5, 2014 at 10:55 Comment(0)
S
0

In my case I was both setting the push segue from the storyboard and programatically. Hopefully that'll help anyone

Shiau answered 20/8, 2014 at 2:40 Comment(0)
P
0

I had this error message too, and the navigation bar and navigation controller transitions were weird. My setup was a bunch of Navigation Controllers embedded in a Tab bar Controller. The problem was that I didn't call super.viewDidLoad() in my Tab bar Controller implementation of viewDidLoad.

Calling super is something the docs clearly point out that you should do when overriding viewDidLoad, and I learned this the hard way.

Maybe this can help someone else too!

Phiz answered 9/10, 2014 at 14:11 Comment(0)
C
0

I know that this was answered, but it could help others.

I had the same problem, but it was caused because I was using a bad event for an info button. I was using "UIControlEventAllTouchEvents" and this generated two push of the same view into the navigation controller. The correct event was "UIControlEventTouchUpInside". I'm new to iOS.

Carousal answered 5/12, 2014 at 12:5 Comment(0)
C
-1

This resolves the problem: https://github.com/nexuspod/SafeTransition

If you push (or pop) a view controller with animation(animated:YES) it doesn't complete right away, and bad things happen if you do another push or pop before the animation completes.

To reproduce this bug, try pushing or popping two view controllers at the same time. Example:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    UIViewController *vc = [[UIViewController alloc] init];
    [self.navigationController pushViewController:vc animated:YES];
}

You will receive this error:

2014-07-03 11:54:25.051 Demo[2840:60b] nested push animation can result in corrupted navigation bar 2014-07-03 11:54:25.406 Demo[2840:60b] Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted.

Just add the code files into your project and makes your navigation controller as a subclass of APBaseNavigationController, and you'll be good to do.

Capitate answered 5/7, 2014 at 16:32 Comment(0)
S
-1

Just to complete the list, here is another reason which can cause "nested push animation can result in corrupted navigation bar":

I did setup several NavigationController within a TabBarController and set the selectedIndex within the storyboard Identifiy Properties. After moving active Tab to Code error disappeared.

Semitics answered 29/4, 2016 at 19:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.