I'm building an iOS app that uses a lot of web services.
The app has a UItabBarController as its root VC.
In one of my controllers I am pushing a viewController onto a UINavigationController. The ViewController that I am pushing onto the stack is a UITableViewController that has a UITableView with data from the web. I reinstaintiate the controller on each level the user goes down in the data. Then, right at the last level I call a web service and then want the whole UITableViewController to be dismissed and the original view controller to be shown.
This is how it looks:
ViewControllerA (UIViewController) -> user taps a Button - Push ViewControllerB onto it. (UINavigationController).
Once I am done with ViewControllerB I need it to be dismissed and ViewControllerA to be shown with the results.
In ViewControllerB I do something like this in NSNotification method:
if ([errorString hasPrefix:#"No descendants"]){
UINavigationController *navigationController =
[self.tabBarController.viewControllers objectAtIndex:1];
BBSearchViewController *searchViewController =
navigationController.viewControllers[0];
searchViewController.categoryId = self.categoryId;
if (searchViewController.brwosingCategoriesFromSearchPage){
[[BBDataStore sharedDataStore]deRegisterForNotifications:self];
[self.navigationController popToRootViewControllerAnimated:NO];
This causes this error in the console:
> 2014-05-08 09:32:16.306 TestApp[36948:60b] Finishing up a navigation transition
in an unexpected state. Navigation Bar subview tree might get corrupted.
2014-05-08 09:32:16.780 TestApp[36948:60b] Unbalanced calls to begin/end
appearance transitions for <ViewControllerA: 0xd08aa00>.
Can someone tell me what I am doing wrong here?
You're getting these errors because you're attempting one transition before another has finished. In your case, it looks like you're getting the notification before the view has finished appearing and then attempting to pop to root. You should be calling popToRootViewControllerAnimated: using user input (when the user taps a button) or at least after viewDidAppear:.
Related
I'm using Xcode 5 with storyboards and I should do something like this:
ViewController with a Start button that launches IntermediateViewController
IntermediateViewController that does an activity and then returns the value to the ViewController.
For the passage ViewController->IntermediateViewController I've set the start button to trigger a push segue. Actions are done and this part seems ok.
Now I have to go back to ViewController passing a string I got in IntermediateViewController methods.
If I use:
ViewController *viewController=[self.navigationController.storyboard instantiateViewControllerWithIdentifier:#"viewController"];
viewController.passedString=_mystring;
[self.navigationController pushViewController:viewController animated:NO];
I get this error:
"Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted"
Is there a way to retrieve my viewController first instance through its identifier or any other solution that will lead the app back to viewController setting also its variable?
Thanks in advance
Even if it's not the exact answer to the question, I solved using this steps:
1) checked in storyboards that each element triggers only one action or segue, not both.
2) calling second view using:
[self.navigationController pushViewController:intermediateViewController animated:NO];
2) going back to previous view.
[self.navigationController popViewControllerAnimated:YES];
3) doing this before popping:
pass string between controllers
I'm currently working on an app that builds a stack of controllers depending on the user.
Basically, I have a UIViewController that has a UIButton leading to another UIView Controller; that has a button leading to another view controller and so on. The view controllers are pushed so that when the user always press the button, I get a stack of multiple view controllers. The views are popped whenever the user wants to go back to the previous view controller.
Everything is working well (push and pop). However, at random instances, the app would crash. I noticed that it happens when there are already a large amount of views pushed, and I suspect that it can be a memory issue.
My question is, other than pushing the view controllers, is there an alternative so that I can avoid stacked views? Could it also be that the crash is not because of the stacked views but because I'm just missing something out? There is no error presented in the logs so I can't find out what's happening and I'm also new to iOS development.
Thank you very much!
Edit 1: There is no error in the logs but when the app crashes, there is this message:
Thread 1: EXC_BAD_ACCESS(code = 1, address = 0xd000000c)
Edit 2: This is how I am pushing the controller:
CustomController *custom = [self.storyboard instantiateViewControllerWithIdentifier:#"Custom"];
[self.navigationController pushViewController:custom animated:YES];
And this is how I popped it when the back button is pressed:
[self.navigationController popViewControllerAnimated:YES];
Edit 3: After enabling zombie objects in the scheme, I started to get this messages after multiple push and pop:
nested push animation can result in corrupted navigation bar
Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted.
Unbalanced calls to begin/end appearance transitions for
Do those messages say that the problem is actually on pushing the controller with animations? Thanks everyone!
Edit 4: I'll try to revise the question to make it more descriptive
This is my setup:
Controller A displays icons that corresponds to different places. You can click on the icon to push Controller B and display details for Location A.
Controller B displays information about Location A, with a button to show Controller A that now displays icons close to location of Location A. Now, you can again click an icon, say for Location B, and display details and so on.
When the user presses the back button, it should display the previous view controller. This is why I used push and pop. Is there a better way to handle this process. Thanks again!
My suggestion is: check if Zombie Objects is enabled and use the instrument "Allocations" to see if your application have, in fact, memory issues. With the informations provided by these tools, you can figure out what is happening with your application and work on it.
Tell me if you need help.
Good luck!
When push or pop, you should turn off animation. I think this causes crash when animation does not finish.
Push: self.navigationController pushViewController:custom animated:NO];
Pop: [self.navigationController popViewControllerAnimated:NO];
My guess is that you push multiple view controllers with animations - this may be a root cause of error. If you push more than one view controller you should animate only LAST push, say:
VC1 *vc1 = [self.storyboard instantiateViewControllerWithIdentifier:#"VC1"];
[self.navigationController pushViewController:vc1 animated:NO];
VC2 *vc2 = [self.storyboard instantiateViewControllerWithIdentifier:#"VC2"];
[self.navigationController pushViewController:vc1 animated:NO];
VC3 *vc3 = [self.storyboard instantiateViewControllerWithIdentifier:#"VC3"];
[self.navigationController pushViewController:vc1 animated:YES];
However, i hardly imagine the situation where the multiple push would be necessary - i think it always leads to bad UX.
I have an application that has a login page. It then moves to a navigation controller that has a collection view as its root view controller. When the app starts and there is only one item in the collection, I want to have the navigation controller automatically push to that item and allow the user to use 'back' to view the collection. This is the same behavior that is in 'Notes' from Apple.
The idea is to allow the user to immediately start to use the app and only 'discover' the need for the collection view after using the app for some time.
I am using IB, storyboards, and segues for my view transitions.
If I programmatically have the root view controller do a performSegue in its viewWillLoad: I get an error about causing a transition while a transition is still in process.
If I move the code calling performSegue into the didLoad, then the user sees a double transition.
A navigation controller usually manages the underlying stack itself. That is, you pass it an initial view controller, and then through segues or pushViewController:animated: the stack is altered. However, there is nothing stopping you from manually altering the stack. In prepareForSegue:sender:, you can check the number of items, and if it's 1, do something like this:
UIViewController *singleItemViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"SingleItem"];
NSMutableArray *viewControllers = [sender.destinationViewController.viewControllers mutableCopy];
[viewControllers addObject:singleItemViewController];
sender.destinationViewController.viewControllers = viewControllers;
Now instead of starting at the first view controller of the stack, you start at the second one, with a back button to navigate back.
Actually, I found that the following worked the best for me.
if (/* reason to change back list*/) {
UIViewController * rootViewController = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"backViewController"];
NSArray * viewControllers = #[rootViewController, self];
[self.navigationController setViewControllers:viewControllers animated:NO];
}
setViewControllers seems to be the best way to manipulate the stack.
I've got a UIViewController with two button, prev and next. When I push these button I re-instantiate the same viewcontroller passing it some variables and then I push it on the UINavigationController with no animation.
With this method my memory is always full cause, as far as i know, it is released only when I pop my viewcontrollers.
So my question is.. how could I implement this behavior? I need to free the memory when I load next (or prev) view controller
Build a custom Navigation controller and animate the transitions yourself.
I have a root view controller (RVC) that opens up a Modal ViewController (MVC). I then navigate within the MVC to few more VC's via a push. What is the best practice to get from one of those VC's back to the RVC?
Normally I have a delegate from the Modal VC that calls up to the RVC which then dismisses the modal, but if you navigate away from it, but I'm not sure how I would do that if you navigate away from it.
Without seeing any code it is a bit hard to help but let me shoot in the dark here.
I will assume that the first controller presented inside the modal view provides the protocol/delegate to call the dismiss action.
If you use UINavigationController inside your modal view to push other view controllers on the stack you can always obtain the first controller like this
UIViewController * yourFirstController = [[[self navigationController] viewControllers] objectAtIndex:0];
// and then use your delegate to call your dismiss method
// you will need to typecast your controller based on your subclass otherwise will get warning here
if ([[yourFirstController delegate] respondsToSelector:#selector(yourCloseProtocolMethod)]) {
[[yourFirstController delegate] yourCloseProtocolMethod];
}
Don't forget that a delegate doesn't have to be a property of a UIViewController inside your model navigation stack. Consider creating a singleton class that holds a reference to the rootviewcontroller as a delegate. That way any class in your application has access to it and you aren't forced to continually pass it through to every UIViewController that requires it.