I have an iOS 5 Tabbed Application, using Storyboards.
My Tabbar Controller points to three Navigation Controllers.
From one of them, the flow looks like this:
Start view --> Photo view (modal) --> Catalog view
On the photo screen, I have a button with the following code:
- (IBAction)acceptPhotoButtonPressed:(id)sender {
UIViewController *catalogView = [self.storyboard instantiateViewControllerWithIdentifier:#"CatalogView"];
[self.navigationController pushViewController:catalogView animated:YES];
}
I've tried fooling around with presentingViewController, parentViewController - even type casted those to a UINavigationController. That causes it to crash, with the following error message:
2012-04-06 00:32:45.808 myapp[19345:707] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UITabBarController pushViewController:animated:]: unrecognized selector sent to instance 0x18d0d0'
So that tells me that I haven't got hold of a UINavigationController, but a UITabBarController.
Is there any way around this?
A "push" style segue can only be done from a view controller that is being managed by a UINavigationController. If you try to do so otherwise nothing will happen.
Instead of displaying your Photo view modally as you describe in your question, you should display an instance of UINavigationController as the modal view and make the Photo View the root view controller of the navigation view. (This can all be set up through storyboard). Then your push segue will work.
If you do not wish for the top navigation bar to appear on your first view controller (Photo view)
you can use:
[self.navigationController setNavigationBarHidden:YES animated:NO]
This will hide top nav bar. Once you push a new view controller, if you want the nav bar to reappear on that one and any subsequent view controllers you'll have to set setNavigationBarHidden to NO on the new viewcontroller.
[self.navigationController setNavigationBarHidden:NO animated:NO]
Segue's are the preferred way of transitioning from one scene to another.
You could either create a Segue from that button or from the VC itself and connect that segue to the scene you want to push. Make sure the Segue property is set to Push and then in the button's IBAction (assuming you connected to the VC) do this:
- (IBAction)acceptPhotoButtonPressed:(id)sender {
[self performSegueWithIdentifier#"mySegueID" sender:nil];
}
then in the prepareForSegue method:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString#"mySegueID"]) {
// Do whatever setup you need to do before firing the segue
}
}
If you connect the Segue to the button itself, then you can eliminate the performSegue method altogether (you really don't even need that IBAction).
Related
I know this has been asked a million times, but I couldn't find a suitable answer in those many questions that I've examined.
I have a custom view controller, and I'm trying to display the view controller when the user taps a button (so no "infamous viewDidLoad problem" here).
Here is my code that runs when the user taps the button: (I have the NIB for the view controller, and I have a navigation controller)
ICLoginViewController *loginViewController = [[ICLoginViewController alloc] initWithNibName:#"ICLoginViewController" bundle:[NSBundle mainBundle]];
//assuming we have a navigation controller.
UINavigationController *navigationController= (UINavigationController*)[[UIApplication sharedApplication] keyWindow].rootViewController;
[navigationController.topViewController presentViewController:loginViewController animated:YES completion:nil];
I'm getting the Warning: Attempt to present <ICLoginViewController: 0xa08a810> on <UINavigationController: 0xa45de70> whose view is not in the window hierarchy! error when I try to present the view controller. Nothing happens on screen. If I tap multiple times I get the same error, and still nothing happens. I've set a breakpoint and verified that navigationController and navigationController.topViewController are not nil. I' using storyboard (if it helps) but not for the custom view controller that I'm trying to display. (I want to make it an app-independent library in the long run, so I'm not referencing any app-specific modules within) Why am I getting this error?
I've found the solution. The problem was, my modally displayed view controller was not the 'top' view controller in navigation controller. If I change the calling view controller to be pushed instead of being modal, then it becomes the top view controller and my app works well. Apparently, this had nothing to do with my custom view controller, but my navigation stack.
If its in an NSObject create a method inside the NSObject that takes your current viewController as an argument and present it there.
eg:
-(void)presentInViewController:(UIViewController *)controller{
ICLoginViewController *loginViewController = [[ICLoginViewController alloc] initWithNibName:#"ICLoginViewController" bundle:[NSBundle mainBundle]];
[controller presentViewController:loginViewController animated:YES completion:^(BOOL comp){}];
}
This way you can call that view controller wherever you want instead of trying to find your way through the navigation stack from UIApplication.
I have a little problem with segues in my app. I am trying to manual PUSH segue. But navigation item/controller/bar is not visible on target controller. When i use button and segue with that button to target view controller, navigation bar IS visible :/
My code is simple:
[self performSegueWithIdentifier:#"MySegue" sender:self];
MySegue is push segue from root view controller of UINavigationController to target controller.
It even did not work with this one
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Storyboard" bundle:[NSBundle mainBundle]];
UIViewController *controller = [storyboard instantiateViewControllerWithIdentifier:#"TargetViewController"];
[self.navigationController pushViewController:controller animated:YES];
and even if i set top bar (navigation item) in story board manualy
thanks for any help:)
Delete the segue that goes from StaznostiViewController to StaznostViewController. Control-drag from the StaznostiViewController object (in the bar below the view) to the StaznostViewController, NOT from the StaznostiViewController view or from the StaznostiViewController tableview prototype cell. Select Push style. Name your segue whatever you wish.
In your code, in the target-action method that you have defined for your dynamic button(s), this is where you call the method performSegueWithIdentifier.
Also, make sure that in the properties for StaznostViewController, you have the property for Top Bar set to Inferred.
Same thing happened to me. I created a "Push" segue from storyboard, and was calling it from code. Then it lost navigation items on the navigation bar. When I changed the segue to "Modal", then navigation items of the destination view appeared properly. .
try nil sender instead of self:
[self performSegueWithIdentifier:#"MySegue" sender:nil];
hope it works
So I've got a UITabBarController as a view in my app, the first tab of which is a UINavigationController. I have a button inside this view that pushes another, custom view onto the stack:
myViewController *vc = [[myViewController alloc] init];
[self pushViewController:myOtherView animated:YES];
The class myViewController has things that are supposed to happen inside of both -viewDidLoad and -viewDidAppear:(BOOL)animated, but if I hit my button right after the UITabBarController view appears, neither of those methods seem to be called. And even stranger, when I hit "Back", the view does not animate away, but rather the view underneath it in the stack just pops back into place.
However, if I go to another tab in the tab bar, then go back to the first tab, then hit my button again, my custom view controller animates in, -viewDidAppear:(BOOL)animated is called, and it animates out of view upon hitting "Back" like it should. Unfortunately, -viewDidLoad is never called.
I'm really trying to get away from using Interface Builder for everything; I want to create this view controller purely programatically, but these weird issues aren't helping. Can anyone think of a reason for this bizarre behavior?
Edit: Even if I create my view controller via IB, this behavior still occurs. What's the deal? Do I need to do something to the UITabBarController?
Edit #2: Here's how I want my views to bet set up:
UITabBarControler
Tab 1: UINavigationController
UIViewController to be pushed onto the stack
UIViewController to be pushed onto the stack
etc (possibly more UIViewControllers)
Tab 2: UIViewController
Tab 3: UINavigation Controller
UIViewController to be pushed onto the stack
UIViewController to be pushed onto the stack
You don't say what kind of object contains the code you posted but, if it's handling a button action, it's probably a custom view controller that's managed by your navigation controller. If that's true, then you'd want [self.navigationController pushViewController:myOtherView animated:YES];.
(If self is some other kind of object or self.navigationController is nil, then you would need to add some more details about your current view controller structure.)
I have a UITableViewController. When you select a cell, it calls init on a UIViewController, which programmatically creates a bunch of views and adds them to the screen. Everything here works fine.
As the user interacts with the app, the views are moved or deleted. I want to have a button where the user can "Start Over" and the UIViewController will init and draw itself like new on the screen. Basically I want the same behavior as if the user went "back" to the UITableViewController and clicked on that same item again.
I can create the button and wire it up and everything. What I need to know is how to release and re-initialize the UIViewController.
How do I do that?
Create your UIViews in UIMyViewController controller.
and use the below code for pushing your view controller in navigation stack.
-(void) buttonClcked:(id) sender
{
//Create for pushing another view controller in navigation stack.
UIMyViewController *myViewController = [[UIMyViewController alloc] init];
//to push view controller in navigation stack.
[self.navigationController pushViewController:myViewController animated:YES];
// you could release it because now it's retained by your UINavigationController
[myViewController release];
myViewController = nil;
}
Well, seems to me, you have two choices:
You can exit the UITableViewController (via a delegate call to the parent) and have it destroy it and relaunch it. (or)
You can put your view building code into a separate routine (not a NIB or loadView or ViewDidLoad or even ViewDidAppear, and then release all the subViews of self.view and call the view builder again.
I have an UIView added in the main window with a controller. On clik of a button on this view I want to load a UINavigationController which will migrate to multiple views pushing them one by one on stack. Now what I want to do is when user reaches at the end of views, in the last view I have a done button. ON clik of this button I want to move back to my first screen unloading the NavigationController from the memory.
What is the best way to do it since popToRootViewController takes you to the first screen of UINavigationController which is my second screen.
You basically want to remove the navigation controllers view, so why cant you just say [navigationController.view removeFromSuperView] ?
One way to do this is to present the navigation controller as a modal view controller, and dismiss it when you're done:
// In the parent controller, when the navigation controller is about to appear:
UINavigationController* navController = [[UINavigationController alloc] init];
[self presentModalViewController:navController animated:YES];
// ... later, in the nav controller, when it's done being used:
[self.parentViewController dismissModalViewControllerAnimated:YES];
[self autorelease]; // goodbye, cruel world (when the ar pool is drained)
A few ideas, in order of desirability
make Controller #1 the root view controller of the stack and then use popToRootViewController. Is there a good reason why you aren't doing this already? Keep in mind you can easily hide the navigation bar from any controller, if that's what you're afraid of.
Add a method called "destroyNavigationStack" or something to main Controller #1 and have a reference to controller #1 in your app delegate. In your Nth view controller, when "done" is hit, get a reference to your app delegate (UIApplication's sharedApplication method), and send View Controller #1 this "destroy" message. There really is no reason to even think about popping view controllers off of the stack since you just want to get rid of the entire stack anyway.
Make ViewController #1 a singleton and call destroyNavigationStack