Is there a better way to hide the backBarButtonItem than this? - iphone

I have a way of hiding the back button used by the navigation controller. It's set by the previous controller, not the one managing the current view, and that makes it tricky to get to. I needed to do this in editing mode so that I could prevent the user from navigating away from the screen.
if(self.editing) {
// Get rid of the back button
UIView *emptyView = [[UIView alloc] init];;
UIBarButtonItem *emptyButton = [[[UIBarButtonItem alloc] initWithCustomView:emptyView] autorelease];
[self.navigationItem setLeftBarButtonItem:emptyButton animated:YES];
} else {
// Restore the back button
[self.navigationItem setLeftBarButtonItem:nil animated:YES];
}
Is there a better way to do this?

use this to hide back button
[self.navigationItem setHidesBackButton:YES]
use this to show back button
[self.navigationItem setHidesBackButton:NO]

Here's the method I use in my view controller to show and hide the back button when editing is enabled and disabled:
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
if (editing) {
// Disable the back button
[self.navigationItem setHidesBackButton:YES animated:YES];
}
else {
// Enable the back button
[self.navigationItem setHidesBackButton:NO animated:YES];
}
[super setEditing:editing animated:animated];
}

Make an outlet with strong (not weak as default) of the bar button from the storyboard to your view controller.
The purpose is not to loose the reference when you set the left/right bar button to nil.

Swift5:
self.navigationItem.setHidesBackButton(true, animated: false)

Related

Enable Edit Mode in ViewController

I'm new on iOS development, i m trying to show a user profile inside a view, and in the same time i want to give a user a possibility to edit his profile by tapping an "Edit" button on the UINavigationBar like shown on Apple web site : Enabling Edit Mode in a View Controller
I tried to find a tutorial explaining all this, but i didn't fin anything. can somebody help me plz, by giving me a link for a tutorial, or a sample code ?
PS : I m using storyboard.
Thanks a lot !
Here and here are a couple of examples for a UITableview
The concept is the same. You add a UIBarButtonItem and change the current mode of the tableView and the status(text) of the buttonItem to show the editing dashes and other content if you choose.
Here is a simple edit mode button press to send the tableView into edit mode to allow for easy deleting. You can also
- (IBAction)editPressed:(id)sender
{
// If the tableView is editing, change the barButton title to Edit and change the style
if (_theTableView.isEditing) {
UIBarButtonItem *newButton = [[UIBarButtonItem alloc]initWithTitle:#"Edit" style:UIBarButtonSystemItemDone target:self action:#selector(editPressed:)];
self.navigationItem.rightBarButtonItem = newButton;
_buttonEdit = newButton;
[_theTableView setEditing:NO animated:YES];
}
// Else change it to Done style
else {
UIBarButtonItem *newButton = [[UIBarButtonItem alloc]initWithTitle:#"Done" style:UIBarButtonSystemItemEdit target:self action:#selector(editPressed:)];
self.navigationItem.rightBarButtonItem = newButton;
_buttonEdit = newButton;
[_theTableView setEditing:YES animated:YES];
}
}
-(void)setEditing:(BOOL)editing animated:(BOOL)animated
{
[super setEditing:editing animated:animated];
// You could do other things in here based on whether editing is true or not
}
You can set default edit button to you navigationItem barbutton inside viewDidLoad as shown below.
-(void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
As give in the apple document
editButtonItem - Returns a bar button item that toggles its title and associated state between Edit and Done. The default button action invokes the setEditing:animated: method.
Override setEditing:animated: in your view controller as shown below.
-(void)setEditing:(BOOL)editing animated:(BOOL)animated
{
[super setEditing:editing animated:animated];
}
You can make use of bool variable editing to achieve your requirements.

How to show back button on the RootViewController of the UINavigationController?

Here is my code. I want to put a back button on the opening rootviewController.
- (void)selectSurah:(id)sender {
SurahTableViewController * surahTableViewController = [[SurahTableViewController alloc] initWithNibName:#"SurahTableViewController" bundle:nil];
surahTableViewController.navigationItem.title=#"Surah";
surahTableViewController.navigationItem.backBarButtonItem.title=#"Back";
UINavigationController *aNavigationController = [[UINavigationController alloc] initWithRootViewController:surahTableViewController];
[self presentModalViewController:aNavigationController animated:YES];
}
I don't believe it's possible to pop the root view controller off the navigation stack, but you can fake it with a UIButton added as the custom view of a UIBarButtonItem:
UIButton *b = [[UIButton alloc]initWithButtonType:UIButtonTypeCustom];
[b setImage:[UIImage imageNamed:#"BackImage.png"] forState:UIControlStateNormal];
[b addTarget:self action:#selector(back:) forControlEvents:UIControlEventTouchUpInside];
self.leftBarButtonItem = [[UIBarButtonItem alloc]initWithCustomView:b];
A suitable PSD of iOS UI elements can be found here.
Faizan,
Helium3 comment makes sense.
I suppose that your button is needed to dismiss the controller presented modally, is it true? Correct if I'm wrong.
If so, you could just create a new UIBarButtonItem and set is a left (or right) button for the UINavigationController navigationItem. To not break encapsulation create it in the viewDidLoad method for your SurahTableViewController controller.
- (void)viewDidLoad
{
[super viewDidLoad];
// make attention to memory leak if you don't use ARC!!!
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Close"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(close:)];
}
-(void)close:(id)sender
{
// to dismiss use dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion
// dismissModalViewControllerAnimated: is deprecated
[self dismissViewControllerAnimated:YES completion:^{ NSLog(#"controller dismissed"); }];
}
Since the SurahTableViewController is a root view controller in a navigation controller you can't go back to the root because you're already there. Since you've presented it modally from something else, you need to put a button on the nav bar that has an IBAction which calls:
[self dismissModalViewControllerAnimated:YES];
Appearance and behavior of a back button in a UINavigationController relies on interaction between a stack of UINavigationControllers. Putting a back button on the first controller breaks this convention, there's nothing to go back to, which is why your code isn't working.
You'll need to manually add UIBarButtonItem to the title bar code like:
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Back" style:UIBarButtonItemStylePlain target:self action:#selector(back:)];
If you truly want it to look like a back button, you'll need to manually create the UIBarButtonItem with an image that mirrors the back button.
Another suggestion though, as it looks like you are attempting to use a back button to dismiss a modal view controller, I'd stick with something more conventional like a "Close" or "Done" button to close the modal view controller. A back button is really more appropriate for navigating a stack of UINavigationControllers.
Here's the answer I think everyone was looking for.
Simply provide an empty UIViewController as the rootViewController of your UINavigationController subclass in a custom initializer. Then push your actual rootViewController without animation before presenting:
init(backButtonRootViewController: UIViewController) {
super.init(rootViewController: UIViewController())
interactivePopGestureRecognizer?.isEnabled = false
pushViewController(backButtonRootViewController, animated: false)
}
You'll also want to disable the interactivePopGestureRecognizer to prevent the user from swiping back to the empty rootViewController.
Then in extend your subclass to implement the shouldPop method of UINavigationBarDelegate and dismiss the navigation controller when the back button is pressed on the rootViewController:
func navigationBar(_ navigationBar: UINavigationBar, shouldPop item: UINavigationItem) -> Bool {
if children.count >= 2 && children[1] == topViewController {
presentingViewController?.dismiss(animated: true, completion: nil)
return false
} else {
return true
}
}
Works like a charm!
For Swift, try something like:
override func viewDidLoad() {
super.viewDidLoad();
self.navigationItem.hidesBackButton = true;
let backButton = UIBarButtonItem(
image: UIImage(named: "my_back_asset"), style: .plain,
target: self, action: #selector(self.onBack));
self.navigationItem.leftBarButtonItem = backButton;
}
#objc private func onBack() {
let controller = self.navigationController?.popViewController(animated: true);
if controller == nil {
// Handle pressing back when on root here.
}
}
Note to change "my_back_asset" to your own back-button image.

UINavigationItem BackBarButtonItem not replaced

I'm having a curious issue with backBarButtonItem. I want to replace its title in the entire application for "Back" and replacing the back button works in most of -viewDidLoad event but in other views it's not working and show the name of the previous view. Has someone has the same problem?
P.S. The way to replace the backBarButtonItem is the standard one instantiating an UIBarButtonItem and setting it to viewController.navigationIten.backBarButtonItem property.
The backBarButtonItem does not set the back button that is shown in the current view, it sets the back button that navigates to the current view, i.e. the back button in the next view.
This makes sense because the back button's title is usually the title of the previous view controller.
If you want to set the left button in the navigation bar directly, use self.navigationItem.leftBarButtonItem.
when you push the view from your current view at that time after allocate your next viewcontroller object ,just put bellow line
YourViewController *objView = [[YourViewController alloc] initWithNibName:#"YourViewController" bundle:nil];
self.navigationItem.title=#"Back";
[self.navigationController pushViewController:objView animated:YES];
your Next View will Appear with Back Button....
:)
Well, at last I've found the solution to this issue.
If you want that any backBarButtonItem of your application has the same title a good approach is to subclass UINavigationController and override - (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated to replace the back button.
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
UIBarButtonItem *_backButton = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(#"BackButtonLabel", "")
style:UIBarButtonItemStyleDone
target:nil
action:nil];
viewController.navigationItem.backBarButtonItem = _backButton;
_backButton = nil;
[_backButton release];
[super pushViewController:viewController animated:animated];
}
By this way every back button in your application will have the same title.
I hope this will be helpful for anyone else.

How to change a UIBarButtonItem in a UINavigationBar

I'm trying to set up a list of items that can be edited. I have a main view, with a UINavigationBar at the top and a UITableView directly under it. I'd like to have my "edit" button change to a "done" button on click, but I can't figure out how to do it.
If I could do it in the code (not it interface builder), I could just replace it, but I can't even do that. I've seen some code using [self.navigationItem], but in my case self is a UIView.
It also feels a bit odd to be using a UINavigationBar when I don't want navigation (this is one page only), but I want a toolbar with a title and and a button, so I don't think really have a choice.
I create one button that can change from Edit to Done. It's a tip from More iPhone Development book.
- (void)viewDidLoad {
[super viewDidLoad];
UIBarButtonItem *editButton = self.editButtonItem;
[editButton setTarget:self];
[editButton setAction:#selector(toggleEdit)];
self.navigationItem.leftBarButtonItem = editButton;
}
And the method toggleEdit
- (IBAction)toggleEdit {
BOOL editing = !self.tableView.editing;
self.navigationItem.rightBarButtonItem.enabled = !editing;
if (editing) {
self.navigationItem.leftBarButtonItem.title = NSLocalizedString(#"Done", #"Done");
//Added in the edition for this button has the same color of the UIBarButtonSystemItemDone
self.navigationItem.leftBarButtonItem.style = UIBarButtonItemStyleDone;
} else {
self.navigationItem.leftBarButtonItem.title = NSLocalizedString(#"Edit", #"Edit");
//Added in the edition for this button has the same color of the UIBarButtonSystemItemDone
self.navigationItem.leftBarButtonItem.style = UIBarButtonItemStylePlain;
}
[self.tableView setEditing:editing animated:YES];
}
Then you don't need replace any of them.
When you use self.editButtonItem, you don't need to change the style and text of the button, it is done automatically. Try removing that code, it will still work :)
set up nav button in IB with the following linked in IB and create an outlet called editOutlet and and an action called editToggle in your header file and your method is as easy as this:
-(IBAction) editToggle:(id) sender {
if (self.tableViewOutlet.isEditing == NO) {
self.editOutlet.title = NSLocalizedString(#"Done", #"Done");
self.editOutlet.style = UIBarButtonItemStyleDone;
[self.tableViewOutlet setEditing:YES animated:YES];
}else {
self.editOutlet.title = NSLocalizedString(#"Edit", #"Edit");
self.editOutlet.style = UIBarButtonItemStylePlain;
[self.tableViewOutlet setEditing:NO animated:YES];
}
}
Got it! Looks like you can get to the UINavigationItem by using the topItem property. Just had to read through the docs, like always!
you can try it
[self setValue:viewController.navigationItem forKey:#"_navigationItem"];
[self.navigationController setNavigationBarHidden:YES animated:NO];
[self.navigationController setNavigationBarHidden:NO animated:NO];

Back button of Navigation Controller does not work!

For some reason, if I try to go back to the main menu using the back button on the upper left corner, only the title returns to the previous menu, but not the view controller. View controller would return to the previous menu only if I explicitly call popViewControllerAnimated using some other button.
Is there anyway to solve this? I think I've coded something wrong. Tried googling but couldn't find any cases like mine.
I'm getting the exact same problem. Here is my code:
- (IBAction) showGameView:(id) sender {
gameView = [[TCGameViewController alloc] initWithNibName:#"TCGameViewController" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:gameView animated:YES];
[gameView release];
}
And when I am done with gameView, I do this:
[self.navigationController setNavigationBarHidden:NO animated:YES];
But all it does when I push the 'back' button is cycle through the navigation bar, but never pops the view. I don't even know how to debug it.
In my other view, "infoView" I call the same code as before except the NavBar is never hidden, but it works just fine.
helps!
This problem can occur when you override the following method in your custom view controller:
- (UINavigationItem*)navigationItem
But you don't specify a UIBarButtonItem for the leftBarButtonItem property of the returned UINavigationItem.
If you use a custom navigationItem, and want the standard back button functionality, you could add a method as follows (remember that every UIViewController has a reference to the navigationController that containts it):
- (void)backButtonTapped
{
[self.navigationController popViewControllerAnimated:YES];
}
And then setup part of the custom navigationItem as follows:
- (UINavigationItem*)navigationItem
{
UIBarButtonItem* newLeftBarButton = [[UIBarButtonItem alloc] initWithTitle:#"Back"
style:UIBarButtonItemStyleBordered
target:self
action:#selector(backButtonTapped)];
UINavigationItem* navigationItem = [[[UINavigationItem alloc] init] autorelease];
Hope this helps.