On my side menu I call a NavigationController for each item on didSelectRowAtIndexPath.
Because of that I have a few NavigationControllers. So I created a custom UINavigationController to reuse the code.
The thing is that my UINavigationController subclass is being called but nothing appears on the simulator.
[self.sideMenuViewController setContentViewController:[self.storyboard instantiateViewControllerWithIdentifier:#"EventsXib"]];
EventsXib is my CustomNavigationController
Any idea?
For the record, the whole point for me to do this is that I want the same leftBarButtonItem and 2 rightBarButtonItems on all my ViewControllers.
UPDATED:
I noticed that this actually worked:
self.navigationItem.leftBarButtonItem = self.navigationController.navigationItem.leftBarButtonItem;
self.navigationItem.rightBarButtonItems = self.navigationController.navigationItem.rightBarButtonItems;
But I still have to do this in every viewController, and thats not what I want.
Here is a general idea of what it looks like:
As #GoGreen suggested, I created a base view controller with the corresponding buttons on the navigationItem.
Its not the simple solution I had in my mind but it works pretty good.
Related
Here is my set up in storyboard.
I'm trying to use this code: (In an IBAction connected to the UIBarButtonItem in the last VC):
- (IBAction)confirmClicked:(UIBarButtonItem *)sender
{
//EXECUTE NAVIGATION
UITabBarController * tabControl = [self.storyboard instantiateViewControllerWithIdentifier:#"TabBarControl"];
tabControl.selectedIndex = 1;
[self presentViewController:tabControl animated:YES completion:nil];
}
to navigate from the last ViewController in this picture to that very first UINavigationController in the stack. I was informed that UINavigationControllers are not meant to be nested like this, and that only one UINavigationController should be necessary, but when I successfully remove(which I have done at least 5 times) the other three UINavigationControllers from the rest of the stack, I completely lose my UINavigationBars from the regular ViewControllers.
The problem with navigating the way that I currently am with my 'confirmClicked:' method, is that I create another instance of that first UIViewController in the hierarchy.
Should I:
A) Remove all the unnecessary UINavigationControllera in the view hierarchy, then use 'popToRootViewController:' to correctly navigate from the last VC to the first?
B) Try to navigate another way from the last VC to the first VC, keeping the extra UINavigationControllers in the view hierarchy?
Also, if I remove the Navigation Controllers, how will I keep my navigation bars in the UIViewControllers, since they have been disappearing in the past when removing the Navigation Controllers?
Sorry in advance for the many questions but I have been stuck for a while.
OK
First thing
Remove all those navigation controllers. You only need one.
Second
The code in that IBAction is creating a brand new instance of the TabBarController and placing it over the current stack (this is how to get memory problems).
Third
What you probably want is either an unwind segue (possibly) or code something like this...
- (IBAction)confirmClicked:(UIBarButtonItem *)sender
{
//EXECUTE NAVIGATION
UITabBarController * tabControl = [self.tabBarController setSelectedIndex:1];
}
I have the following code:
NSArray* stack = self.navigationController.viewControllers;
NSArray* newStack = #[stack[0], stack[2]];
[self.navigationController setViewControllers:newStack animated:NO];
stack contains 3 view controllers. The problem is that the navigation bar is not removing UINavigationItems to match, so self.navigationController.navigationBar.items.count still returns 3 after running this code. Going back gets you into a weird state where you have a back button at the top that you can press but it just disappears, not taking you back any further.
Is this a bug in iOS 7 or am I just trying to do something really stupid? What's the best way to fix or work around this?
The navigationBar has its own ‘items’ stack which is not updated until viewDidAppear hits.
Which means, if we recreate the navigation controllers’ stack in viewDidLoad using i.e. setViewControllers: when we get to viewDidAppear we will have the current item added to the bars’ ‘items’ stack and therefore the UINavController viewController stack will not be in sync with the UINavBar items stack. This appears to be an iOS 7 bug.
In iOS 6.0 the 2 different stacks do not get out of sync no matter where we set the new viewControllers stack.
So try moving your code in viewDidAppear and see if that fixes the problem. I bet it will, because for me it did.
The behavior you are describing is a corrupt navigation controller stack. This is probably because you are trying to use a navigationController improperly. I don't have much context from the code here, but I am guessing you are trying to skip back to your root view controller? I think this is probably more what you would need:
https://developer.apple.com/library/ios/documentation/uikit/reference/UINavigationController_Class/Reference/Reference.html#//apple_ref/occ/instm/UINavigationController/popToRootViewControllerAnimated:
you will then want to add a custom back button with something like so:
UIBarButtonItem* backButton = [[UIBarButtonItem alloc] initWithTitle:#"Back" style:UIBarButtonItemStyleBordered target:self action:#selector(yourMethodToPopToRoot)];
//yourMethodToPopToRoot is a method you create that runs popToRootViewController
self.navigationItem.leftBarButtonItem = backButton;
Any questions let me know.
I have a TableViewController that can be transitioned to from two other view controllers; one transitions by segue, and the other programmatically. Everything works well when transitioning via segue, but when the TableViewController appears after a programmatic transition, the UIBarButtonItem is missing and the cell segue doesn't work (it should take the user to a new screen).
I'm guessing that because the UIBarButtonItem and cell segue were setup in the Storyboard, they aren't loaded when the TableViewController is loaded programmatically. So, I'm assuming that I need to load them manually somewhere. My question is where the appropriate place to do this is?
Thanks for reading.
EDIT:
Here's the code for the transition:
OtherTVC *otherTVC=[[OtherTVC alloc] init];
[self.navigationController pushViewController:otherTVC animated:YES];
This is linked to the callout accessory of a map pin.
To get an instance of the controller as it was created in the storyboard, try giving it an identifier and using instantiateViewControllerWithIdentifier: rather than the alloc/init route. If your current controller where the code is running came from the same storyboard, you can get a reference to it through the controller's storyboard property in order to make that call.
I have a UIViewController classes A and B. A loads B using: [A.view addSubView B.view].
B has a navigation bar with a Back button. I want to go back to A when I click it, so in the selector I tried [self.view removeFromSuperview], but it only removed the navigation bar. Then I tried [self.view.superview removeFromSuperview], it still just removed the navigation bar. What should I do?
Also, another minor issue with the Back button: setting it's title. I tried these two ways, but it still displays "Back".
navItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Chapter" style:UIBarButtonItemStylePlain target:self action:#selector(handleBackBarButtonItem:)];
navItem.backBarButtonItem.title = #"Chapter";
Thank you in advance!
I don't think you quite understand how navigation (with UINavigationController) works in iOS. Assuming you want animation, this is what you want:
Set up a UINavigationController. This can be done in the app's delegate (to avoid memory leakage, set an instance variable on UINavigationController *navController:
navController = [[UINavigationController alloc] initWithRootViewController:A];
Note that we are adding A as our root view controller.
Push the second view controller when needed. I assume that you are adding B.view after a button is clicked or something. In the implementation of the method that adds the second view controller, run the following code, instead of [A.view addSubview:B.view]. This method should be in the first controller's .m file:
[self.navigationController pushViewController:B animated:YES];
This will also give a nice transition effect.
Pop the second view controller off the stack. With UINavigationController, a pretty arrow-shaped back button is automatically included in a pushed view controller, to navigate back to the last view controller. This means that you don't even need any code to allow backward navigation.
That's it! Now if you need to change the title of B's back button, do this in A's viewDidLoad method:
self.navigationItem.backBarButtonItem = customBackButtonItem;
You can get an array of subviews and then remove the ones you wanted to be removed. This SO post will show you how to remove all subviews or multiple subviews using subviews array.
I am an iOS development newbie. I am using the following code to set my backBarButtonItem -
UIBarButtonItem *temporaryBarButtonItem=[[UIBarButtonItem alloc] init];
temporaryBarButtonItem.title=#"Back";
self.navigationItem.backBarButtonItem = temporaryBarButtonItem;
[temporaryBarButtonItem release];
I also want it to execute another function, apart from going back. Is that possible? Or do I need another button to save?
You can practically do that in your viewDidDisappear or viewWillDisappear method.
If this view can only go back and doesn't present any views, then this should work.
However, if you plan on presenting a subview, modal view, go deeper in the navigation hierarchy, or do other view operations that will cause viewDidDisapper to get called, then you'll need to separate your back-button code logic somehow.
For instance if you will present a modal view from this view, you can check if self.modalViewController is nil, if it is then you have no modal view being present and can safely execute the back-button code. If it is not nil then you have a modal view present and should not execute back-button code. (viewWillDisappear should register the modal view controller as not-nil).
Very simple, try this!
[[UIBarButtonItem alloc] initWithTitle:#"Done" style:UIBarButtonItemStyleDone target:self action:#selector(myCoolAction:)];
Then all you do is change myCoolAction: to an appropriate method in your view controller and handle it. Enjoy!
EDIT: Oh, if you want to use this for the backBarButtonItem, it won't work how you expect. The system will not call your target/actions for the backBarButtonItem. They get cleared once assigned to the backBarButtonItem.
One way to handle this is too hook up the your UINavigationController as a delegate. Then in your
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
Method you can do some custom behavior there.
Strictly speaking, this cannot be done with UIKit.
See the docs on backBarButtonItem:
The target and action of the back bar button item you set should be
nil. The default value is a bar button item displaying the navigation
item’s title.
The work around is to specify a leftBarButtonItem and attach whatever custom behaviour you require by attaching a #selector target.
The tricky bit is the button's image itself. As you know the default is not a rectangular button. Rather it has a left side arrow shape. For this you'll need to set a custom image to make the button appear to be the default.
There's tons of resources to do that out there including this one to extract all the UIKit artwork:
https://github.com/0xced/UIKit-Artwork-Extractor
You can add UIBarButtonItem in xib and add handler event there.
Else create UIBarButtonItem with a custom view, which is an UIButton and add event handler.
You should be able to do something like this instead of using viewDidDisappear or viewWillDisappear.
Place this in viewDidLoad:
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Cancel" style:UIBarButtonItemStyleDone target:self action:#selector(cancelButtonAction:)];
self.navigationItem.leftItemsSupplementBackButton = NO;
Then you can create the method cancelButtonAction cancelButtonAction for your custom code and the back functionality like this:
- (void)cancelButtonAction:(id)sender {
//Your custom code goes here...
//This will perform the back functionality if using a Navigation Controller
[self.navigationController popViewControllerAnimated:YES];
}
Note: this will end up using a rectangular button, without the arrow.