I have a little problem using the navigation bar in an iOS project using objective-c.
I have some view controllers which are managed by a navigationviewcontroller, in a hierarchy like 1-2-3.
For the Viewcontrollers 2 and 3, I define custom back buttons in the viewWillAppear function, as I need to assign them a more complex logic when touching them. This is the reason why I do not set the back buttons in the previous view controller.
Everything works fine like this, my single problem is that, on transition from one view to another, the arrow of the back button is overlayed by a little view in the color of the navigation bar's background color until up to 50%, and then disappears when the transition has finished.
I create my back buttons in the navigationbar with this function, which is called in viewWillAppear:
- (void)setupCustomBackButton:(NSString *)title
action:(SEL)action
buttonFrame:(CGRect)buttonFrame {
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
backButton.frame = buttonFrame;
[backButton setImage:[UIImage imageNamed:#"backArrowOwn"] forState:UIControlStateNormal];
backButton.imageEdgeInsets = UIEdgeInsetsMake(0.0f, -8.0f, 0.0f, 0.0f);
[backButton setTitle:title forState:UIControlStateNormal];
[backButton.titleLabel setFont:[UIFont systemFontOfSize:17.0]];
backButton.titleEdgeInsets = UIEdgeInsetsMake(0.0f, -3.0f, 0.0f, 0.0f);
[backButton setContentHorizontalAlignment:UIControlContentHorizontalAlignmentLeft];
[backButton addTarget:self action:action forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithCustomView:backButton];
self.navigationItem.leftBarButtonItem = barButton;
}
I hope that maybe someone has a good hint for me! Thanks!
After some long trying, I finally managed to save the problem on my own.
So basically, when I create a leftBarButton, the button does not begin at the left margin, but has some space to the left margin. When I now inset my button image with a negative inset, the image will be basically outside my button frame. This portion is then weirdly overlayed on transition.
To fix this, I just add another UIBarButtonItem additionally to my leftBarButtonItems. This seems to fix the issue for me, although I do not really know why exactly.
Also, I think that it is weird that the leftBarButton in the navigationBar does not begin exactly at the margin, but has a space to the margin and I have to inset my image in order to get the exact same position for the image, as the default back button has.
My new code for creating a custom back button now looks like this:
- (void)setupCustomBackButton:(NSString *)title
action:(SEL)action
buttonFrame:(CGRect)buttonFrame {
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
backButton.frame = buttonFrame;
[backButton setImage:[UIImage imageNamed:#"customBackArrow"] forState:UIControlStateNormal];
backButton.imageEdgeInsets = UIEdgeInsetsMake(0.0f, -8.0f, 0.0f, 0.0f);
[backButton setTitle:title forState:UIControlStateNormal];
[backButton.titleLabel setFont:[UIFont systemFontOfSize:17.0]];
backButton.titleEdgeInsets = UIEdgeInsetsMake(0.0f, -3.0f, 0.0f, 0.0f);
[backButton setContentHorizontalAlignment:UIControlContentHorizontalAlignmentLeft];
[backButton addTarget:self action:action forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithCustomView:backButton];
UIBarButtonItem *negativeSpacer = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace
target:nil action:nil];
self.navigationItem.leftBarButtonItem = nil;
[self.navigationItem setLeftBarButtonItems:[NSArray arrayWithObjects:negativeSpacer,barButton, nil] animated:NO];
}
Thanks everyone and I hope that my answer helps anyone else too!
Try to move the custom button initialization to the viewDidLoad method.
Related
Is it possible to change the back button on a UINavigationBar and change the title?
When I try to set the customView property, I get an image right next to the default button.
I use this code
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithImage:backButtonImage landscapeImagePhone:nil style:UIBarButtonItemStylePlain target:nil action:nil];
I want the title to be "Back" which is easy enough to do in Storyboard. But the problem is that no matter if I use code above or use customView property, the default back button remains.
You can add a UIImage to the UIButton. And, then use it as a custom back button. Here's a quick example:
// Custom image
UIImage *backButtonImage = [UIImage imageNamed:#"button-background-image.png"];
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
[backButton setFrame:CGRectMake(0, 0, 100, 30)];
[backButton setBackgroundImage:backButtonImage forState:UIControlStateNormal];
// Custom title
[backButton setTitle:#"Back" forState:UIControlStateNormal];
[backButton setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
[backButton addTarget:self action:#selector(barPayButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
[backButton setShowsTouchWhenHighlighted:NO];
UIBarButtonItem *buttonOnBar =[[UIBarButtonItem alloc] initWithCustomView:backButton];
self.navigationItem.leftBarButtonItem = buttonOnBar;
Note: You will loose the chevron (system-provided back arrow) which was introduced in iOS7. It goes as a title and chevron together presenting your previous view controller.
Update:
You can also use UIEdgeInsets to resize your image intelligently.
UIEdgeInsets edgeInsets = UIEdgeInsetsMake(0, 0, 15, 10);
UIImage *backButtonImage = [[UIImage imageNamed:#"button-background-image.png"] resizableImageWithCapInsets:edgeInsets];
You can achieve what you want by setting the navigationItem.leftBarButtonItem of the view controller that's being pushed to the custom bar button item with the look you want. Good Luck!
backButton = [UIButton buttonWithType:UIButtonTypeCustom];
[backButton addTarget:self action:#selector(gotoAmphorasViewController) forControlEvents:UIControlEventTouchUpInside];
[backButton setFrame:CGRectMake(0.0f,0.0f, 44,44)];
The problem i am facing is that though the button dimensions are44*44, wherever i tap anywhere around it, the the button action is fired.
Please try the bellow code : its working properly
- (void)viewDidLoad {
[super viewDidLoad];
UIImage *buttonImage = [UIImage imageNamed:#"back.png"];
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setImage:buttonImage forState:UIControlStateNormal];
button.frame = CGRectMake(0, 0, buttonImage.size.width, buttonImage.size.height);
[button addTarget:self action:#selector(back) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *customBarItem = [[UIBarButtonItem alloc] initWithCustomView:button];
self.navigationItem.leftBarButtonItem = customBarItem;
[customBarItem release];
}
-(void)back {
[self.navigationController popViewControllerAnimated:YES];
}
Its not a bug. It is a default behaviour. In iPhone, for navigation bar buttons the touch detection is little more expanded rather than its frame. Just have a look on any other application. Everywhere the button get fired if we tap nearer but outside its frame.
It's the intended behaviour, if you really want to limit the area of the touch, you can wrap the button inside a UIView:
UIView *buttonContainer = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 44, 44)];
[buttonContainer addSubview:button];
_barButton = [[UIBarButtonItem alloc] initWithCustomView:buttonContainer];
I cannot figure out how to disable the back-button animation that occurs in the navigation bar when switching from a tableview to a standard view (when a cell is selected). There is no obvious line of code that enabled animation to begin with. Here it is in gif-form:
The navigation buttons in the Facebook app do not animate, so it is possible.
It may be relevant to mention that I am using the ViewDeck library to create the Facebook-like tableView menu, i.e. swipe to the right to expose a table.
EDIT: solution is based on Hesham Abd-Elmegid's answer but modified to use a custom image...
UIImage *settingsImage = [UIImage imageNamed:#"back_button#2x.png"];
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
backButton.frame = CGRectMake(280.0, 10.0, 29.0, 29.0);
[backButton setBackgroundImage:settingsImage forState:UIControlStateNormal];
backButton.frame = CGRectMake(0, 0, 50, 30);
[backButton addTarget:self action:#selector(goBack) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *customBarItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
self.navigationItem.leftBarButtonItem = customBarItem;
If you set a custom UIBarButtonItem as a left navigation item (instead of standard back button item), it will fade instead of slide in, just like in Facebook's app. Just create a simple method that will replace back button functionality by calling popViewControllerAnimated: on the navigation controller in which your detail view controller is contained.
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:#"Back" style:UIBarButtonItemStyleBordered target:self action:#selector(goBack)] autorelease];
}
- (void)goBack
{
[self.navigationController popViewControllerAnimated:YES];
}
Note: UIBarButtonItem can also be set up with an image using initWithImage:style:target:action: method.
You could replace the back button with a custom UIButton. That way it won't animate on transition.
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
[button setTitle:#"Back" forState:UIControlStateNormal];
backButton.frame = CGRectMake(0, 0, 50, 30);
[backButton addTarget:self action:#selector(onBack) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *customBarItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
self.navigationItem.leftBarButtonItem = customBarItem;
[customBarItem release];
You will have to find a PNG for the arrow shape of the back button though.
In my RootViewController, I set an image as a rightBarButtonItem exactly like this, but in this second ViewController I created, this "back" button won't display. I run it on the simulator and on my iPhone and I can tap the area where the button should be showing up and the goBack{} method is being called. Just the image(s) aren't showing. Any thoughts?
Also, as far as the images go, I'm exporting them the exact same way I've been exporting all my other images. All the others have been working fine. It doesn't seem like a problem with the images, as I've substituted the back button images for several others and none of them are displaying. I'm pretty sure I'm creating these images in the right order. Anyway, any help would be much appreciated.
From my NewGameViewController.m file:
- (void)viewDidLoad {
[super viewDidLoad];
CGFloat startingPoint = 0.0;
CGRect bounds = self.view.bounds;
bounds.origin.y = startingPoint;
bounds.size.height -= startingPoint;
self.ivarNewGameTableView = [[UITableView alloc] initWithFrame:bounds style:UITableViewStyleGrouped];
self.ivarNewGameTableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
self.ivarNewGameTableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:self.ivarNewGameTableView];
UIImageView *backgroundImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"background.png"]];
self.ivarNewGameTableView.backgroundView = backgroundImageView;
UIImage *mainNavBar = [UIImage imageNamed:#"startNewGameNavBar.png"];
UIImageView *navBarView = [[UIImageView alloc] initWithImage:mainNavBar];
[navBarView setFrame:CGRectMake(0.0, 0.0, 320.0, 44.0)];
[self.navigationController.navigationBar addSubview:navBarView];
UIButton *back = [UIButton buttonWithType:UIButtonTypeCustom];
// neither one of the following lines work. I know they're supposed to be identical, but
// seen Xcode be picky.
back.frame = CGRectMake(0.0, 0.0, 54.0, 36.0);
// [back setFrame:CGRectMake(0.0, 0.0, 54.0, 36.0)];
[back addTarget:self action:#selector(goBack) forControlEvents:UIControlEventTouchUpInside];
[back setImage:[UIImage imageNamed:#"back.png"] forState:UIControlStateNormal];
[back setImage:[UIImage imageNamed:#"backTouched.png"] forState:UIControlStateHighlighted];
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithCustomView:back];
self.navigationItem.leftBarButtonItem = backButton;
}
-(void)goBack {
NSLog(#"goBack called.");
// [self.ivarNewGameTableView pushViewController:];
}
If you're in a navigation stack the back button item is set for you. Use backBarButtonItem correctly do get the behaviour you like.
On your main view controller that you want to come back to you need to overload the backBarButton item.
UIBarButtonItem *newBackButton = [[UIBarButtonItem alloc] initWithTitle: #"Back" style: UIBarButtonItemStyleBordered target: nil action: nil];
self.navigationItem.backBarButtonItem = newBackButton;
Some code samples on github
Ok. Finally figured this out. The problem was with this line in the creation of my New Game View Navigation Bar:
[self.navigationController.navigationBar addSubview:navBarView];
I did this in my Root View's Navigation Bar too. It now looks like this, in both files:
UIImage *startNewGameNavBar = [UIImage imageNamed:#"startNewGameNavBar.png"];
// with the key line being this one
[self.navigationController.navigationBar setBackgroundImage:startNewGameNavBar forBarMetrics:UIBarMetricsDefault];
In noob-speak, as I wish more programmers would use for noobs like me, if you're having problems with buttons disappearing behind the your custom navigation bar, and you're using code that looks like my example, it's because you're not supposed to be adding it as a subview. This puts it on top of everything else. Instead, change the navigation bar's background image and, of course, the image winds up in the...(drumroll, please)...background. (end drumroll)
As for the adding of the button images, I just created a UIButton with custom type, set the images for the normal and highlighted states, set the button to call my goBack method, set its frame size, and assigned it to the navigationItem's leftBarButtonItem property:
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
[backButton setImage:[UIImage imageNamed:#"back.png"] forState:UIControlStateNormal];
[backButton setImage:[UIImage imageNamed:#"backTouched.png"] forState:UIControlStateHighlighted];
[backButton addTarget:self action:#selector(goBack) forControlEvents:UIControlEventTouchUpInside];
[backButton setFrame:CGRectMake(0.0, 0.0, 54.0, 36.0)];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
Hey, I've written a class (A) which inherits some functionality including an implementation of a navigation button. Class A has both a view and edit mode, I want to only show the button when am in edit mode. So far I've not been able to remove this button and I don't really want to create to another class just for edit.
Also other classes inherit this functionality so I don't really want to be messing about with parent.
The code that I use to create the button is below:
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *buttonImage = [UIImage imageNamed:#"button.png"];
[button addTarget:self
action:#selector(buttonPressed:)
forControlEvents:UIControlEventTouchUpInside];
button.bounds = CGRectMake(0, 0, buttonImage.size.width, buttonImage.size.height);
[button setBackgroundImage:buttonImage forState:UIControlStateNormal];
[button setTitle:NSLocalizedString(#"BUTTON", #"")
forState:UIControlStateNormal];
LPRBSLabel *buttonLabel = [[LPRBSLabel alloc] initWithStyle:UICustomeButtonTitle];
[button setTitleEdgeInsets:UIEdgeInsetsMake(0.0, 0.0, -5.0, 0.0)];
button.titleLabel.font = buttonLabel.font;
[button setTitleColor:buttonLabel.textColor forState:UIControlStateNormal];
[buttonLabel release];
UIBarButtonItem *barLeftInfoButton = [[UIBarButtonItem alloc] initWithCustomView:button];
self.navigationItem.leftBarButtonItem = barLeftInfoButton;
[barLeftInfoButton release];
I managed to solve it using:
self.navigationItem.leftBarButtonItem = nil;
I had a mind freeze and was using the above statement before the button had actually when created :-(
It would be easier to set the NavigationItem's property "hidesBackButton" to yes:
http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UINavigationItem_Class/Reference/UINavigationItem.html#//apple_ref/occ/cl/UINavigationItem
Another option to consider is to disable the button until you want it to function. It will be visible, but dimmed.
self.navigationItem.leftBarButtonItem.enabled = NO;
To Remove Button from navigation bar just assign tag to button and write a below code
[[self.navigationController.navigationBar viewWithTag:0106] removeFromSuperview];