I am a newbie to iOS world and have started building custom code on top of a templated code.
So excuse me for the obvious.
The View chain starts with a MainWindow.xib which contains a App Delegate Object, a Window Object and Application ViewController. I dont understand why those objects are needed over there. But what I understand, I need to mention starting ViewController in the "Nib Name" Property to initiate my custom View Controller (called "EmptyViewController"). Its a dummy view controller, just there to avoid crash to happen as a result of missing valid viewcontroller.
I initiate a separate Modal View Controller(MainViewController) inside didFinishLaunchingWithOptions.
Code for initiating modal View Controller --
self.window.rootViewController = self.viewController;
mainView = [[MainViewController alloc] initWithNibName:#"MainViewController" bundle:nil];
// present the viewcontroller
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:mainView];
[self.viewController presentModalViewController:navController animated:NO];
// release it, because it's retained as modalViewController
[navController release];
I do not put this MainViewController inside MainWindow.xib as I want to have navigation at the root of MainViewController.
Inside MainViewController, I push HelpViewController when "help" button is pressed.
But HelpViewController does not show any navigation bar. I do not understand why?
Code for Pushing Navigation bar --
HelpViewController *helpVC = [[HelpViewController alloc] init];
[self.navigationController pushViewController:helpVC animated:YES];
[helpVC release];
So I would like to understand --
1) Why is MainWindow.xib needed? Can I remove it? (Note: I tried to remove it, but then I get blank screen)
1.a) Why are all the controls/objects App Delegate Object, a Window Object and Application ViewController objects needed?
2) Why doesnt HelpViewController show Navigation bar?
3) Another thing I noticed, if I say self.presentingViewController, EmptyViewController handle is returned while popViewController returns me back to MainViewController.
Thanks
The App Delegate simply implements some app-level 'callbacks' by which iOS communicates with your own code. In main.m you can see how iOS is told which of your classes implements UIApplicationDelegate. iOS creates an instance of this class and call these delegate methods ('callback') whenever appropriate (e.g. when the app goes to background).
The Window is something iOS provides, your app needs to tell what to display on it. And, as you saw, this is usually done in didFinishLaunchingWithOptions (which is called by iOS to inform your app things are ready to get started).
A View Controller is a class that handles states of stuff you show on the Window. You don't show stuff directly on the Window, but instead use Views. Every View Controller has a View with UI elements.
The XIB or NIB is a UI description/layout file. A XIB and View are linked together; you need to tell the XIB to which View Controller member (e.g. a UILabel) a UI element belongs, and you tell the XIB which View Controller method to call on a certain UI event (e.g. user taps on a button).
These are the basics. I'm aware it does not answer all your questions; I suggest you read the very good Apple documentation. Don't try to understand everything immediately as things, as you're experiencing, indeed can seem illogical at start.
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?
Calling pushViewController before viewDidAppear is unsafe.
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.
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.
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.
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.
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!
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.
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.
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.
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.
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
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.
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.
My Solution was
[self performSelector:#selector(moveTo) withObject:nil
afterDelay:0.5];
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.
In my case I was both setting the push segue from the storyboard and programatically. Hopefully that'll help anyone
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!
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.
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.
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.
I'm new to iPhone development, and multiple views (xib or nib) are really confusing me. This is what i'm trying to achieve...
using TabBarControllerAppDelegate
Have 5 different TabBar Items and have created 5 different Views thru the TabBarController
The First View has a UIButton that is a Next button that needs to go to another view called View2.XIB.
I setup a new UIViewController which references the View2 and an IBAction for the switchPage, etc but not able to get it to do anything when clicking on the button.
All of My TabBar buttons work but not able to Navigate to anything outside of the Tabbar
Any help in this regard will be highly appreciated. Anyone have any examples
IBAction switchPageButtonPressed:(id)sender
{
[self.tabbarcontroller.tabBar setSelectedItem:[self.tabbarcontroller.tabBar.items objectAtIndex:1]];
here 1 means ur 2nd tabbar
}
It is difficult to find the problem without the code, but I will assume your action code for the switchPage button is incorrect. You should use code similar to the following:
- IBAction switchPageButtonPressed:(id)sender
{
ViewController2 *view2VC = [[ViewController2 alloc] initWithNibName:#"View2" bundle:nil];
[self presentModalViewController:nview2VC animated:YES];
[view2VC release];
}
If you are confident your method works, then you will want to verify that the action is hooked up correctly. The easiest way to do this is to place a breakpoint on the method and run the app in Debug. When you click the button, the debugger should break on your method, if it doesn't, you will need to check your connections in Interface Builder.
I created a new Navigation Based Application project.Then in MainWindow.xib I added a button to the navigationbar. I would like to push a new View onto the screen where I can enter information, which will be added as an object to the array of the UITableView.
But I don't know where to write the IBAction to link the button to (Appdelegate or the RootViewController)? Because as you see in the screenshot, it resides in MainWindow.xib because the RootViewController is merely a Table and doesn't contain the navigation. But in the document view of MainWindow.xib it is located under the RootViewController.
Do I have to create a new View Controller inside the XIB as well and create an IBOutlet for it?
I tried putting the code inside my AppDelegate and reference the button to the delegate but it doesn't work.
Please help. Thanks in advance.
See the screenshot here: http://i56.tinypic.com/5djbcm.png
When you ask yourself a question "where does this action belong" it's most probably a controller because controllers handle event flows in your app. Next question - "What controller is in charge when this action happens? What controller is most interested in this action?". Answer in your case is root table controller (RootViewController instance). Create an IBAction method in it which will push form controller (one you use to enter information) to navigation controller.
// somewhere in RootViewController.m
- (IBAction)addNewEntry {
NewEntryFormController *c = [[[NewEntryFormController alloc] init] autorelease];
// ...
[self.navigationController pushViewController:c animated:YES];
}
my project first used the Three20 frame work for handling navigation.But due to some problems i ha to remove the three20 part.So im redesigning my app.
what im trying to do is show a detail view when user touches a button.I created a new mainwindow as there was none earlier.It does not have a navigation controller.
i used
DetailViewController *detailViewController=[[DetailViewController alloc] init];
[self.navigationController pushviewController:detailviewController animated:YES]
in the buttos touch up event.
but when i run the app nothings hapens on button touch.No warnings or errors are shown.
Ive done this in other apps but it just doesnt work here.
Since you are not using a UINavigationController, the navigationController property of the your UIViewController will remain nil. "Compile-wise" there's no problem, since the property exists, but the compiler does not know the property will remain nil, hence no warnings/errors are shown.
Sending a message to a nil object is allowed in Objective-C, hence the code doesn't crash and does nothing when being run.
If you want to get your code up & running, you'll need to put the root UIViewController in a UINavigationController. Once the UIViewController is shown in a UINavigationController, the property will be set to the UINavigationController automatically.
Edit: You could use something like this, instead of showing someController, "encapsulate" it in a UINavigationController and show that controller.
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:someController];