Similar to the issue described here, with the exception that my TabBar is actually showing the BackButton on my NavigationBar is not. In the question I provided a link to the answer that solved the problem was that there was a NavigationController within a NavigationController, I do not have that so this is a different issue.
The basic flow of my storyboard is Login (UIView) - TabBar (UITabBarController) - NavigationController (UINavigationController) - Actual visible screen (UITableViewController) - New TableView where the issue occurs (UITableViewController).
On the new actual visible screen or the TableView the NavigationBar at the top shows just fine and I can click in the general area that the BackBarButton should be and it will go back, but no BackBarButton item is visible.
I tried changing the color, allocating it in the previous ViewController, making sure it was visible, etc. And none of them have shown the Back Button. It seems to be there but it is not shown.
Any ideas? From the other question that is similar to my issue this seems to be a iOS 9 specific thing.
EDIT from looking at the Debug View Hierarchy I can tell that the back button is in fact there but it just has no label or back arrow on it. How can I make the back label visible?
EDIT 2
Here is the viewDidLoad method of the view controller where the back button should appear.
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#" " style:UIBarButtonItemStylePlain target:nil action:nil];
[self.navigationItem.backBarButtonItem setTintColor:[UIColor whiteColor]];
// Other unimportant stuff
}
The way I am segueing to this view controller is through the storyboard with a Push segue. Code is below.
- (void)segueToUser: (UIButton*)button {
long row = button.tag;
PFObject *PFQuote = [_recent quoteAtIndex:(row-1)/2];
PFUser *u = [PFQuote objectForKey:#"creator"];
_send = u;
[self performSegueWithIdentifier:#"showUser" sender:self];
}
Here is my prepareForSegue in the same file as the segueToUser
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"showUser"]) {
BRETTFUserTableViewController *bfutvc = [segue destinationViewController];
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#" " style:UIBarButtonItemStylePlain target:nil action:nil];
bfutvc.us = _send;
}
}
Here is a picture of a section of my storyboard.
The first view is a tabView
The second view is a navView
The third view is a tableView
The fourth view is a tableView
Since no one has seemed to have found a solution I will provide additional information. From my digging deeper into the Debug View Hierarchy I have found some more information regarding the backButton. I can see that the back arrow is intact there and there is a NSString next to it that seems to be nil, I do not know why it is nil but that is what I found. Here are additional pictures of what I have found.
In your
(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
you have Write a code line
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#" " style:UIBarButtonItemStylePlain target:nil action:nil];
In Above line you have set title " " Empty String.
Instead of empty string use the title which you want to show on back-button item. fr ex:
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Back" style:UIBarButtonItemStylePlain target:nil action:nil];
Check it might that help.
Adding a image of storyboard, how i have embedded the view controller in navigation controller.
or in case if you have put navigation appearance code anywhere in your app then also post that code.
In your viewDidLoad and prepareForSegue methods you have this :
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#" " style:UIBarButtonItemStylePlain target:nil action:nil];
Which replace the default back button of the navigation controller by a button without a label.
You should simply remove these 2 lines to have the default back button.
I have finally solved the issue. To accomplish this I had to go into the navigation bar for the first tableViewController and set the Back Button title to Back and then set the tint color (as it defaulted to clear) and now it works.
I am trying to find a way to stop some of the processes within a detail controller (and let the user know that this is happening) when the back button in the navigation bar is pressed. However, I can't find a way to implement these changes when the button is pressed.
Is there a way to do this?
If you want to raise a user alert/notification before going back, you are really going to have to create your own back bar button item and assign it to self.navigationItem.leftBarButtonItem.
You need to hide the default button using:
self.navigationItem.hidesBackButton = YES;
Then add a target to your new button which does your process cleanup and raises an alert. In the alert handler, pop the controller once the user has acknowledged the alert.
The fastest and easiest way is to use custom back button like below;
-(void)viewDidLoad{
UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Back" style:UIBarButtonItemStyleBordered target:self action:#selector(actionBack)];
[self.navigationItem setBackBarButtonItem:barButtonItem];
}
-(void)actionBack{
//PopViewController
}
Like any other button.
Put this in the .m file.
- (IBAction)saveButton:(id)sender {
//actions
}
Then control drag from the UINavigationBarButton to the IBAction
Following the examples of the many duplicates for this questions, I can't seem to get it right.
I have a UINavigationViewController that has a LoginViewController as the rootViewController. Here I got a button with a segue (push) to a LoginInfoViewController.
In LoginInfoViewController.m:
- (void)viewDidLoad
{
[super viewDidLoad];
//null
NSLog(#"%#", self.navigationItem.backBarButtonItem);
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:#"Test"
style:UIBarButtonItemStyleDone
target:nil
action:nil];
self.navigationItem.backBarButtonItem = backButton;
//not null, still the back button says: "Back"
NSLog(#"%#", self.navigationItem.backBarButtonItem);
}
You will need to set the backBarButtonItem of the controller you are going back to, not the controller you pushed. Move your code to the LoginViewController viewDidLoad method.
The navigation controller derives the back button for the navigation bar from the backBarButtonItem of the preceding controller in the stack. If the item is nil, it will use the value in the title property of same. If the title is too long to fit, the navigation bar may substitute the string "Back" in place of the title.
If your controller has a custom left bar button item, the navigation bar will ignore the backButtonItem property and title presenting the custom button instead.
Set the title of the back button on the view BEFORE. So, if you segue from LoginViewController, you set the back button title on the item before you segue to LoginInfoViewController
Example:
In the viewDidLoad method on LoginViewController:
UIBarButtonItem *newBackButton = [[UIBarButtonItem alloc] initWithTitle: #"Go back" style: UIBarButtonItemStyleBordered target: nil action: nil];
[[self navigationItem] setBackBarButtonItem: newBackButton];
This means you're setting the button on the LoginViewController, not on the LoginInfoViewController.
In my RecordViewController, within the didSelectRowAtIndexPath I push a detailViewController (which inherits from UIViewController):
[[self navigationController] pushViewController:detailViewController animated:YES];
Once the DetailViewController appears I can see a Back navigationButton in the top left corner, which automatically pops the current view controller to get back to the previous ViewController.
Now I need to show a UIAlertView and ask the user, if the data should be saved or not.
And only when the user has made a decision, the current view controller should disappear.
My problem is if I put this code into viewWillDisappear, it is already too late. I can't stop the process while showing the UIAlertView. This needs to be intercepted the moment the user pressed the back button.
Is there a method I could override to achieve this?
Create a UIBarButtonItem:
UIBarButtonItem * backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle: #"Back"
style: UIBarButtonItemStyleDone target: self action: #selector(onBackButtonTapped:)];
Assign it to left bar button item:
self.navigationItem.leftBarButtonItem = backBarButtonItem;
Implement onBackButtonTapped API:
- (void) onBackButtonTapped: (id) sender
{
// Display an UIAlertView
}
You may want to customize the back button. Please look into UIBarButtonItem for more details.
Instead of pushing a detail view controller, the usual way to gather data is to present one modally. That will give you 2 free spaces on the left and right of the (new) navigation bar to place a Save and Cancel button.
UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:detailViewController];
[self presentViewController:navigationController animated:YES completion:nil];
// detailViewController will have to set up buttons in its init
You can use a delegate protocol you create to handle save and cancel actions in the presenting (i.e. not the detail) view controller.
Overview: The idea is to have your own barbutton to intercept the backing out from the VC.
In your viewDidLoad you can do this
UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Back"
style:UIBarButtonItemStyleDone target:self action:#selector(Back:)];
self.navigationItem.backBarButtonItem = backButtonItem;
Then your Back: method can do this
-(void)Back:(id) sender
{
//Your code for showing AlertView with delegate as self. Remember to conform to the UIAlertViewDelegate protocol.
}
Then put your save functionality in
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (selected buttonIndex is the one for saving data)
{
//save your data
}
//popViewController
}
The "back button" of a UINavigationController by default shows the title of the last view in the stack. Is there a way to have custom text in the back button instead?
From this link:
self.navigationItem.backBarButtonItem =
[[UIBarButtonItem alloc] initWithTitle:#"Custom Title"
style:UIBarButtonItemStylePlain
target:nil
action:nil];
As Tyler said in the comments:
don't do this in the visible view controller, but in the view
controller that you'd see if you hit the back button
You can set the text in the Interface Builder:
Select the navigation item of the ViewController that the back button would return to:
In the utilities panel attribute inspector, enter your label for the Back Button:
I would prefer this approach over setting the title in code as in the accepted answer.
Also note, you need to do this in the view controller one level up the
stack. In other words, don't do this in the visible view controller,
but in the view controller that you'd see if you hit the back button.
--Tyler
I use this:
// In the current view controller, not the one that is one level up in the stack
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationController.navigationBar.backItem.title = #"Custom text";
}
I found a handy solution to this by simply setting the title of the controller before pushing another controller onto the stack, like this:
self.navigationItem.title = #"Replacement Title";
[self.navigationController pushViewController:newCtrl animated:YES];
Then, make sure to set the original title in viewWillAppear, like this:
-(void)viewWillAppear:(BOOL)animated
{
...
self.navigationItem.title = #"Original Title";
...
}
This works because the default behavior of UINavigationController when constructing the back button during a push operation is to use the title from the previous controller.
The title of the back button defaults to the previous view's title so a quick trick I use is to place the following code on the previous view's .m file.
-(void)viewWillAppear:(BOOL)animated {
// Set title
self.navigationItem.title=#"Original Title";
}
-(void)viewWillDisappear:(BOOL)animated {
// Set title
self.navigationItem.title=#"Back";
}
in your init method, add the following code:
- (id)initWithStyle:(UITableViewStyle)style {
if(self = [super init]) {
//...
UIBarButtonItem *customBackButton = [[UIBarButtonItem alloc] initWithTitle:#"Back"
style:UIBarButtonItemStylePlain
target:self
action:#selector(goBack)];
self.navigationItem.leftBarButtonItem = customBackButton;
[customBackButton release];
//...
}
return self;
}
then add a simple method, to allow viewcontroller dismissing:
-(void)goBack {
[self.navigationController popViewControllerAnimated:YES];
}
Add the following code in viewDidLoad or loadView
self.navigationController.navigationBar.topItem.title = #"Custom text";
I tested it in iPhone and iPad with iOS 9
Adding to rein's answer. Note from Apple's docs that the declaration of backBarButtonItem is this:
#property(nonatomic, retain) UIBarButtonItem *backBarButtonItem
Therefore, rein's answer will leak memory because the synthesized setter will retain the instance you pass it, which is never released explicitly. You can remedy this by using autorelease
self.navigationItem.backBarButtonItem =
[[[UIBarButtonItem alloc] initWithTitle:#"Custom Title"
style:UIBarButtonItemStyleBordered
target:nil
action:nil] autorelease]; //<-- autoreleased
Or you could point a variable at the instance so you can explicitly release it later:
UIBarButtonItem* item = ...
self.navigationItem.backBarButtonItem = item;
[item release];
Hope this helps!
- (void)viewDidLoad {
[super viewDidLoad];
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:#"Back" style:UIBarButtonItemStylePlain target:nil action:nil];
self.navigationItem.backBarButtonItem = backButton;
[backButton release];
}
I've discovered something interesting.
If you subclass the UINavigationController and override the pushViewController:animated: method and do something like this: (bear in mind that I'm using ARC)
UIBarButtonItem *backButton = [[UIBarButtonItem alloc]
initWithTitle: #"Back"
style: UIBarButtonItemStyleBordered
target: nil action: nil];
viewController.navigationItem.backBarButtonItem = backButton;
[super pushViewController:viewController animated:animated];
Then for all ViewControllers that are pushed with your navigation controller will have the "Back" button in them automatically. If you want to change the text for certain view controllers you can try and maybe cast the viewcontroller to a certain class or your own custom protocol (which your viewcontroller inherits from which could have a method like backButtonText or something silly like that) which can give you certain information on the viewcontroller that's coming in sothat you can customize the back button text for it. Now the back button text is taken care of in a place which should hold the responsibility solely. I have to admit that creating a new button to change the text sucks, but oh well.
Can anyone think of a reason why not to do it like this? Atleast you don't have to fiddle with viewcontroller titles or have to remember to create a new back button before pushing the viewcontroller on the navigation controller.
rein's answer works well.
Note that if you push more than one view controller, the changed back button title will appear for each of them, which may not be what you want.
In that case, you'll need to create the custom UIBarButtonItem each time you push a view controller.
Also, make sure you do it before pushing the view controller, otherwise you will get a screen hiccup as the title changes.
Expanding on Aubrey's suggestion, you can do this in the child view controller:
create two variables for storing the old values of the parent's navigationItem.title and the parent's navigationItem
UINavigationItem* oldItem;
NSString* oldTitle;
in viewDidLoad, add the following:
oldItem = self.navigationController.navigationBar.topItem;
oldTitle = oldItem.title;
[oldItem setTitle: #"Back"];
in viewWillDisappear, add the following:
[oldItem setTitle: oldTitle];
oldTitle = nil; // do this if you have retained oldTitle
oldItem = nil; // do this if you have retained oldItem
It's not perfect. You will see the the title of the parent view change as the new controller is animated in. BUT this does achieve the goal of custom labeling the back button and keeping it shaped like a standard back button.
Put this into you viewDidLoad, hope it will result into what you are looking for
UIBarButtonItem *backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Close"
style:UIBarButtonItemStylePlain target:nil action:nil];
self.navigationItem.backBarButtonItem = backBarButtonItem;
[backBarButtonItem release];
if You want to set title in ARRIVING controller (sometimes more logic..)
in swift 3 do:
func setBackButtonNavBar(title: String, delay: Double){
let when = DispatchTime.now() + delay
DispatchQueue.main.asyncAfter(deadline: when, execute: { () -> Void in
if let navBar = self.navigationController?.navigationBar{
navBar.backItem?.title = title
}
})
}
in upcoming controller:
override func viewDidLoad() {
super.viewDidLoad()
self.setBackButtonNavBar(title: "back", delay: 0.3)
}
usually I put self.setBackButtonNavBar in a controller extension.
I know this is an old question and the answers' kind of out updated!
The easy way is to do this in parent ViewController:
i.e the one that takes you to next view controller.
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "Custom text here", style: .plain, target: nil, action: nil)
Doing this in code remove the back button style of the UINavigationConroller. If you add a Navigation Item in each of yours views, you can set the title of the back botton in the StoryBoard.