iOS 7 UIBarButtonItem ridiculous spacing issue - ios

I'm having an issue that so far I cannot find a solution to. I am adding a new feature to my app and wish to add a second UIBarButtonItem on the left side of my UINavigationBar. For some reason iOS 7 takes this as a button1, grandCanyon, button2. I cannot find any way to remove the ridiculous spacing between these two buttons, which is also causing my title to be out of alignment. Can anyone help!? Is there a solution to this!?
Code:
UIBarButtonItem *firstButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"firstButton"] style:UIBarButtonItemStylePlain target:self action:#selector(showSettings)];
UIBarButtonItem *secondButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"secondButton"] style:UIBarButtonItemStylePlain target:self action:#selector(showAttachments)];
[self.navigationItem setLeftBarButtonItems:[NSArray arrayWithObjects:firstButton, secondButton, nil]];

Think I've managed to sort out the problem using a custom view as shown below, it's not perfect (selection dims the buttons darker rather than lighter for example) but I'll try fixing that tomorrow. Just glad my headache is over! Thank you for your help, it lead me to a few new approaches I didn't try.
UIImage *firstButtonImage = [UIImage imageNamed:#"firstButton"];
firstButtonImage = [firstButtonImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
UIButton *firstButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 35, 35)];
[firstButton setImage:firstButtonImage forState:UIControlStateNormal];
[firstButton addTarget:self action:#selector(firstButtonPressed) forControlEvents:UIControlEventTouchUpInside];
UIImage *secondButtonImage = [UIImage imageNamed:#"secondButton"];
secondButtonImage = [secondButtonImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
UIButton *secondButton = [[UIButton alloc] initWithFrame:CGRectMake(45, 0, 35, 35)];
[secondButton setImage:secondButtonImage forState:UIControlStateNormal];
[secondButton addTarget:self action:#selector(secondButtonPressed) forControlEvents:UIControlEventTouchUpInside];
UIView *leftBarItemsView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 80, 35)];
[leftBarItemsView addSubview:firstButton];
[leftBarItemsView addSubview:secondButton];
UIBarButtonItem *leftBarItem = [[UIBarButtonItem alloc] initWithCustomView:leftBarItemsView];
[self.navigationItem setLeftBarButtonItems:[NSArray arrayWithObject:leftBarItem]];

There may be a better way, but to correct spacing issues on bar button items on iOS 7, I've subclassed UINavigationBar and overridden the layoutSubviews method. There you can move each bar button item wherever you want.
As an example:
- (void)layoutSubviews
{
[super layoutSubviews];
// If iOS 7, fix the bar button positions
BOOL isIOS7 = [[[UIDevice currentDevice] systemVersion] compare:#"7" options:NSNumericSearch] != NSOrderedAscending;
if (isIOS7)
{
for (UIBarButtonItem *item in self.topItem.leftBarButtonItems)
{
// Reposition the customView property
}
for (UIBarButtonItem *item in self.topItem.rightBarButtonItems)
{
// Reposition the customView property
}
}
}
Actually, as I looked at my code, I was using UIBarButtonItems with custom views. So I was able to move the custom view position.
You will likely need to loop through the subviews of the UINavigationBar to move them if you're just using UIBarButtonItems with images like this:
- (void)layoutSubviews
{
[super layoutSubviews];
// If iOS 7, fix the bar button positions
BOOL isIOS7 = [[[UIDevice currentDevice] systemVersion] compare:#"7" options:NSNumericSearch] != NSOrderedAscending;
if (isIOS7)
{
for (UIView *subview in self.subviews)
{
// Reposition as needed
}
}
}

Related

How can I change size of UINavigationBar in UINavigationViewController in iOS 8?

What is the proper way to change height of navigation bar?
I need to create custom title in navigation bar, it should contains two UILabels one above other. The Title should be resized to fit those UILabels.
Should I override sizeThatFits: method in my custom TitleView, would other buttons correctly change to fit that size? How can I change a size of NavigationBar?
That is what I need to create using latest SDK features.
Create the following class category (you can create it in your implementation file):
#import "objc/runtime.h"
#interface UINavigationBar (CustomHeight)
- (void)setHeight:(CGFloat)height;
#end
static char const *const kHeight = "Height";
#implementation UINavigationBar (CustomHeight)
- (void)setHeight:(CGFloat)height
{
objc_setAssociatedObject(self, kHeight, #(height), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSNumber *)height
{
return objc_getAssociatedObject(self, kHeight);
}
- (CGSize)sizeThatFits:(CGSize)size
{
CGSize newSize;
if (self.height) {
newSize = CGSizeMake(self.superview.bounds.size.width, [self.height floatValue]);
} else {
newSize = [super sizeThatFits:size];
}
return newSize;
}
#end
And after that, simply call [self.navigationController.navigationBar setHeight:100.0] in your viewDidLoad or where you need it. Works in both iOS 7.1 and 8.1.
Disclaimer: Any alteration of the API and its functions is prone to future issues with new OS releases! Apple does not intend to allow us to change the navigation bar height (except for some rare instances) so use any solution wisely after assessing the risks vs advantages.
Looking at that design that you posted, I don't have the impression that the bar is really higher than the standard 44pt. Please note that starting with iOS7 the status bar is integrated into the same bar and the 20pt of the status bar is added to the total height.
IMHO, the only problem you need to solve is to stack the title/subtitle, and that can be easily done with a custom titleView, as Kampai has demonstrated.
Add custom view for both navigation title and navigation right bar button.
Here how you can add title view like above.
// Custom view for navigation title.
UIView *customTitleView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 44)];
UILabel *label1 = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 22)];
label1.text = #"Stasik";
label1.font = [UIFont fontWithName:#"HelveticaNeue-Light" size:20];
label1.textAlignment = NSTextAlignmentCenter;
[customTitleView addSubview:label1];
UILabel *label2 = [[UILabel alloc] initWithFrame:CGRectMake(0, 22, 100, 22)];
label2.text = #"Stasik iPhone";
label2.font = [UIFont fontWithName:#"HelveticaNeue-Light" size:16];
label2.textAlignment = NSTextAlignmentCenter;
label2.textColor = [UIColor darkGrayColor];
[customTitleView addSubview:label2];
self.navigationItem.titleView = customTitleView;
// Custom view for right navigation.
UIButton *upButton = [UIButton buttonWithType:UIButtonTypeCustom];
upButton.frame = CGRectMake(0, 0, 20, 44);
[upButton setImage:[UIImage imageNamed:#"up"] forState:UIControlStateNormal];
[upButton addTarget:self action:#selector(upButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *upButtonBarItem = [[UIBarButtonItem alloc] initWithCustomView:upButton];
UIButton *downButton = [UIButton buttonWithType:UIButtonTypeCustom];
downButton.frame = CGRectMake(0, 0, 20, 44);
[downButton setImage:[UIImage imageNamed:#"down"] forState:UIControlStateNormal];
[downButton addTarget:self action:#selector(downButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *downButtonBarItem = [[UIBarButtonItem alloc] initWithCustomView:downButton];
// Remove trailing space for right view.
UIBarButtonItem *nagativeSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
nagativeSpace.width = -11;
self.navigationItem.rightBarButtonItems = #[nagativeSpace, upButtonBarItem, downButtonBarItem];
// Left item
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
backButton.frame = CGRectMake(0, 0, 40, 44);
[backButton setImage:[UIImage imageNamed:#"back"] forState:UIControlStateNormal];
[backButton addTarget:self action:#selector(backButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
// Remove leading space for left view.
nagativeSpace.width = -15;
UIBarButtonItem *backButtonBarItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
self.navigationItem.leftBarButtonItems = #[nagativeSpace, backButtonBarItem];
OutPut:
Arrow images are different than OP's requirement but it serves layout for navigation views.
From above code make change in nagativeSpace.width value to arrange buttons with accurate distance from left as well as right margins.

My app displaying 2 toolbars instead of one

When i testing my app through xcode in multiple devices, my app shows one toolbar as expected. After that i have uploaded the update of my app, in app store. But suddenly, I realized that in some iphones (ios 7.0.4), after app's update, on launching it cames with 2 toolbars.
I am adding the toolbar programmatically in viewDidLoad function of this controller.
UIImage* leftImg = [UIImage imageNamed:#"left.png"];
UIImage* rightImg = [UIImage imageNamed:#"right.png"];
CGRect frame = CGRectMake(0, 0, leftImg.size.width, leftImg.size.height);
UIButton* lefButton = [[UIButton alloc] initWithFrame:frame];
UIButton* rigButton = [[UIButton alloc] initWithFrame:frame];
[lefButton setTitle:#"" forState:UIControlStateNormal & UIControlStateHighlighted];
[rigButton setTitle:#"" forState:UIControlStateNormal & UIControlStateHighlighted];
[lefButton setImage:leftImg forState:UIControlStateNormal];
[lefButton setImage:leftImg forState:UIControlStateSelected];
[rigButton setImage:rightImg forState:UIControlStateNormal];
[rigButton setImage:rightImg forState:UIControlStateSelected];
[lefButton addTarget:self action:#selector(loadPrevChapter:) forControlEvents:UIControlEventTouchUpInside];
[rigButton addTarget:self action:#selector(loadNextChapter:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *itemLeft = [[UIBarButtonItem alloc] initWithCustomView:lefButton];
UIBarButtonItem *itemRight = [[UIBarButtonItem alloc] initWithCustomView:rigButton];
// In case i want to add Space between barbuttonitems
UIBarButtonItem *flexiableItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
// add items to toolbar
NSArray *items = [NSArray arrayWithObjects:itemLeft, flexiableItem, itemRight, nil];
self.toolbarItems = items;
[self.navigationController setToolbarHidden:NO animated:NO];
UIImage *toolbarBgImage = [UIImage imageNamed:tlbImg];
UIImage *navbarBgImage = ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0f) ? [UIImage imageNamed:navImgIos7] : [UIImage imageNamed:navImg];
[[UINavigationBar appearance] setBackgroundImage:navbarBgImage forBarMetrics:UIBarMetricsDefault];
[[UIToolbar appearance] setBackgroundImage:toolbarBgImage forToolbarPosition:UIBarPositionAny barMetrics:UIBarMetricsDefault];
But i haven't added at navigation controller the toolbar this:
[[self navigationController].view addSubview:_toolbar];
as #Xeieshan said below.
Has anyone notice something like this before or does anyone know why this happening?
Screenshot of my app running on my iPhone 5 (v7.0.4)
Screenshot of my app, installed through appstore, after update, running on an iPhone 5 (v7.0.4)
[[self navigationController].view addSubview:toolbar]; This is how to add UIToolbar in UINavigationController but i cannot see your code where you do it?
I think you are adding UIToolbar on both UIViewController and UINavigationController.
I'd advise against adding the toolbar to UINavigationController.view, as it breaks encapsulation (even though that's not enforced very much in UIKit).
Instead, add a custom root view controller containing the toolbar and a contained UINavigationController. This also allows you to properly layout the toolbar so that it doesn't cover the views in the navigation controller.

iOS 7 custom back button

I want to use custom back button. in iOS 6 everything is perfect but iOS 7 is strange.
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:[[UIImage imageNamed:#"back_button_normal"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 12.0, 0, 12.0)] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
first, it has no iOS 7 arrow and no background image.
(Russian locale)
then, if you press the button background image appears. Also i had background image set for UIControlStateHighlighted state and when you hold the button pressed highlighted image appears too. After any back button once pressed all back buttons have background image.
BUT! If you present modal view controller, dismiss it, then push any view controller - iOS 7 arrow will appear at every back button.
I use DP5. Is that a UIKit bug?
PS Also i tried to create back button manually, using UIBarButtonItem, set background image to it, then self.navigationItem.backBarButtonItem = barButtonItem; Did not help.
Then i tried to set background image to disabled state and change enabled property of my bar button item, did not help too.
This is not a bug, this how Back button looks in iOS 7. For example:
You should probably use the new concept for your application, and not to set background image for back button in iOS 7.
If you still want you back button have the same as it looked in iOS6 than you should probably create those back buttons manually:
- (void)loadView
{
[super loadView];
UIButton *backButton = [[UIButton alloc] initWithFrame: CGRectMake(0, 0, 60.0f, 30.0f)];
UIImage *backImage = [[UIImage imageNamed:#"back_button_normal.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 12.0f, 0, 12.0f)];
[backButton setBackgroundImage:backImage forState:UIControlStateNormal];
[backButton setTitle:#"Back" forState:UIControlStateNormal];
[backButton addTarget:self action:#selector(popBack) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
self.navigationItem.leftBarButtonItem = backButtonItem;
}
-(void) popBack {
[self.navigationController popViewControllerAnimated:YES];
}
Edit: Not to break Swipe Gesture (Here is a source)
self.navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;
The custom background image not appearing on the first push was fixed in iOS 7 GM.
To hide standard back indicator use this code:
if ([UINavigationBar instancesRespondToSelector:#selector(setBackIndicatorImage:)]) { // iOS 7
[navigationBarAppearance setBackIndicatorImage:[UIImage imageNamed:#"transparent_1px"]];
[navigationBarAppearance setBackIndicatorTransitionMaskImage:[UIImage imageNamed:#"transparent_1px"]];
}
The custom background image not appearing initially was not fixed in iOS7 GM or final, as far as I can tell. I see the same problem. It does seem to be an Apple bug; the private view Apple uses simply does not get a setNeedsDisplay call when it needs it on initial display. Doing anything to it which causes that call should fix it -- like pressing on it (which likely changes internal state so it calls setNeedsDisplay on itself), or bringing a modal up (which probably forces a redisplay of the entire view hierarchy on the next viewWillAppear: call).
Using leftBarItems instead also can work, but that may cause a lot of maintenance issues with existing code (some screens may have their own left items, expecting that when set back to nil they restore the original back item, for example).
As mentioned, ideally you would be able to change to a borderless look on iOS7, which means that the bug isn't really apparent (since there is no background image). For some iOS6/iOS7 transition situations though, that may be difficult (lots of screens, and/or the need to support older iOS versions for a while and too hard to have two looks implemented, and it doesn't look good borderless without other changes). If that's the case, the following patch should work:
#import <objc/runtime.h>
#implementation UINavigationBar (BackButtonDisplayFix)
+ (void)load
{
if ([UIDevice currentDevice].systemVersion.intValue >= 7)
{
/*
* We first try to simply add an override version of didAddSubview: to the class. If it
* fails, that means that the class already has its own override implementation of the method
* (which we are expecting in this case), so use a method-swap version instead.
*/
Method didAddMethod = class_getInstanceMethod(self, #selector(_displaybugfixsuper_didAddSubview:));
if (!class_addMethod(self, #selector(didAddSubview:),
method_getImplementation(didAddMethod),
method_getTypeEncoding(didAddMethod)))
{
Method existMethod = class_getInstanceMethod(self, #selector(didAddSubview:));
Method replacement = class_getInstanceMethod(self, #selector(_displaybugfix_didAddSubview:));
method_exchangeImplementations(existMethod, replacement);
}
}
}
- (void)_displaybugfixsuper_didAddSubview:(UIView *)subview
{
[super didAddSubview:subview];
[subview setNeedsDisplay];
}
- (void)_displaybugfix_didAddSubview:(UIView *)subview
{
[self _displaybugfix_didAddSubview:subview]; // calls the existing method
[subview setNeedsDisplay];
}
#end
Note: UINavigationBar does currently have an override of the method in question, so I'd expect the method_exchangeImplementations style to be used. I just added the other stuff for safety in case Apple changes their code. We may go borderless ourselves, but I did find this approach worked as an option (until a more thorough UI uplift), at least.
Additional note: This bug appears to be fixed in iOS 7.1. So, the patch could be conditionalized to only install the methods if running >= 7.0 and < 7.1.
There is a better solution that doesn't involve method swizzling.
You need to add UINavigationViewControllerDelegate method somewhere in your app.
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
dispatch_async(dispatch_get_main_queue(), ^{
[[navigationController.navigationBar subviews] makeObjectsPerformSelector:#selector(setNeedsDisplay)];
});
}
My solution is for iOS 7 and above.
At first, make default back button invisible.
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"" style:UIBarButtonItemStylePlain target:nil action:nil];
then, set default backIndicatorImage of back button using custom image.
[UINavigationBar appearance].backIndicatorImage = [[UIImage imageNamed:#"topbar_icon_back_n.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
[UINavigationBar appearance].backIndicatorTransitionMaskImage = [[UIImage imageNamed:#"topbar_icon_back_p.png"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
At this point, make custom UINavigationBar for resizing _UINavigationBarBackIndicatorView which contains above backIndicatorImage.
const CGPoint SANavigationBarOffset = {-8, 11.5};
#implementation SANavigationBar
- (void)layoutSubviews
{
[super layoutSubviews];
// set back button position
NSArray *classNamesToReposition = #[#"_UINavigationBarBackIndicatorView"];
for (UIView *view in [self subviews]) {
if ([classNamesToReposition containsObject:NSStringFromClass([view class])]) {
CGRect frame = [view frame];
frame.origin.x = 0;
frame.origin.y = 0;
[view setFrame:frame];
}
}
}
#end
then, set it as my navigationBar
// set custom NavagationBar for back button position
[self.navigationController setValue:[[SANavigationBar alloc] init] forKey:#"navigationBar"];
Add button as navigation item in ios7 as below
UIButton *btnAdd = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 60, 30)];
[btnAdd setContentMode:UIViewContentModeScaleAspectFit];
[btnAdd setBackgroundImage:[UIImage imageNamed:#"back.png"] forState:UIControlStateNormal];
[btnAdd addTarget:self action:#selector(backButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *btnAdd = [[UIBarButtonItem alloc] initWithCustomView:imView];
self.navigationItem.rightBarButtonItem = btnAdd;
Using Swift you can just add a extension:
extension UIViewController: UIGestureRecognizerDelegate {
func popBack() {
self.navigationController?.popViewControllerAnimated(true)
}
func enableCustomBackButtom() {
self.navigationItem.leftBarButtonItem = UIBarButtonItem(image: UIImage(named: "icon-back"), style: UIBarButtonItemStyle.Plain, target: self, action:"popBack")
self.navigationController?.interactivePopGestureRecognizer.delegate = self
}
}
And in your UIViewController use like this:
self.enableCustomBackButtom()
I just did it providing the same behaviour as in iOS6 (notice that navigationBar is the UINavigationBar), make sure that navigationBar has a topItem
UINavigationItem *topItemNavigation = [navigationBar topItem];
UIBarButtonItem *barButtonTopItemNavigation = [[UIBarButtonItem alloc] initWithTitle:topItemNavigation.title style:UIBarButtonItemStyleBordered target:nil action:nil];
[barButtonTopItemNavigation setBackButtonBackgroundImage:YOUR_IMAGE_BACKGROUND forState:UIControlStateNormal barMetrics:UIBarMetricsDefault ];
[topItemNavigation setBackBarButtonItem: barButtonTopItemNavigation];
}
My solution was to write a category on UINavigationItem. This is for iOS7.
- (void)mdSetCustomBackButton:(UINavigationController *)navigationController
{
MDBackButton *backButton = [[MDBackButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 44.0, 44.0) navigationController:navigationController];
[backButton addTarget:self action:#selector(popBack:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *barButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
[self setLeftBarButtonItem:barButtonItem];
[navigationController.interactivePopGestureRecognizer setDelegate:(id<UIGestureRecognizerDelegate>)self];
}
- (void)popBack:(MDBackButton *)sender
{
[sender.navigationController popViewControllerAnimated:YES];
}
And subclass UIButton to add a UINavigationController property (to pop and set swipe back delegate).
#property (nonatomic, weak) UINavigationController *navigationController;
#implementation MDBackButton
- (id)initWithFrame:(CGRect)frame navigationController:(UINavigationController *)navigationController
{
self = [super initWithFrame:frame];
if(self){
_navigationController = navigationController;
[self setImage:[UIImage imageNamed:#"back_button"] forState:UIControlStateNormal];
}
return self;
}
This is work for me:
- (void)setCustomNavigationBackButton
{
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"" style:UIBarButtonItemStylePlain target:nil action:nil];
UIImage *myIcon = [self imageWithImage:[UIImage imageNamed:#"backbutton.png"] scaledToSize:CGSizeMake(20, 20)];
self.navigationController.navigationBar.backIndicatorImage = myIcon;
self.navigationController.navigationBar.backIndicatorTransitionMaskImage = myIcon;
}
- (UIImage *)imageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize
{
//UIGraphicsBeginImageContext(newSize);
// In next line, pass 0.0 to use the current device's pixel scaling factor (and thus account for Retina resolution).
// Pass 1.0 to force exact pixel size.
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
Also, custom font with custom color:
//self.navigationController.navigationBar.tintColor = [UIColor whiteColor];
[[UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], nil] setTitleTextAttributes:
#{NSForegroundColorAttributeName:[UIColor whiteColor],
NSFontAttributeName:[UIFont fontWithName:#"Signika-Bold" size:20]}
forState:UIControlStateNormal];
Reference: https://stackoverflow.com/a/2658801/1371949
I use these codes below, which works in iOS 8
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.translatesAutoresizingMaskIntoConstraints = NO;
button.exclusiveTouch = YES;
button.titleLabel.font = [UIFont systemFontOfSize:14.0];
[button setTitleColor:kWhiteColor forState:UIControlStateNormal];
[button setTitleColor:[UIColor colorWithRed:1/255.0 green:36/255.0 blue:60/255.0 alpha:1.0] forState:UIControlStateHighlighted];
[button setTitle:#"Back" forState:UIControlStateNormal];
[button setImage:[UIImage imageNamed:#"barbutton_back"] forState:UIControlStateNormal];
[button setImageEdgeInsets:UIEdgeInsetsMake(1.0, 0.0, 0.0, 0.0)];
CGSize fontSize = [button.titleLabel sizeThatFits:CGSizeMake(100.0, 30.0)];
button.frame = CGRectMake(0.0, 0.0, button.imageView.image.size.width+fontSize.width, 30.0);
UIBarButtonItem *barbtn = [[UIBarButtonItem alloc] initWithCustomView:button];
//fix iOS 7 left margin
UIBarButtonItem *negativeSpacer = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
negativeSpacer.width = -10;
self.navigationItem.leftBarButtonItems = [NSArray arrayWithObjects:negativeSpacer,barbtn, nil];
-(void) viewWillAppear:(BOOL)animated
{
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
[btn setFrame:CGRectMake(0, 0, 30, 44)];
[btn setImage:[UIImage imageNamed:#"btnBack.png"] forState:UIControlStateNormal];
[btn addTarget:self action:#selector(PopToView) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *btnBack = [[UIBarButtonItem alloc] initWithCustomView:btn];
[btnBack setTintColor:[UIColor whiteColor]];
[[self.navigationController navigationItem] setLeftBarButtonItem:btnBack];
}

UINavigationController Navigation Bar Image is hiding other backbuttons and custom images?

- (UINavigationController *)navigationController {
nav = [[UINavigationController alloc]
initWithRootViewController:[self demoController]];
UIImage *image;
// If Iphone/iPod Touch
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
// If iPhone 5 or new iPod Touch
if([UIScreen mainScreen].bounds.size.height == 568){
image = [UIImage imageNamed:#"nav.png"];
} else{
// Regular iPhone
image = [UIImage imageNamed:#"nav.png"];
}
}
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[nav.navigationBar addSubview:imageView];
return nav;
}
As I try to add leftBarButtonItem as
- (UIBarButtonItem *)leftMenuBarButtonItem {
UIButton *backButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
[backButton setBackgroundImage:[UIImage imageNamed:#"main_menu.png"] forState:UIControlStateNormal];
UIBarButtonItem *barBackButtonItem = [[UIBarButtonItem alloc] initWithCustomView: backButton];
[backButton addTarget:self action:#selector(leftSideMenuButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
return barBackButtonItem;
}
It doesn't display main_menu.png on navigation, but if comment this code
// [nav.navigationBar addSubview:imageView];
it will show main_menu.png, what is issue, why nav.png is now showing other images, it is added as SubView, so what should I do to display others left or right bar button items, over nav.png.
Yo shouldn't be adding the image to an image view and adding that as a subviews. Instead you should be calling setBackgroundImage:forBarMetrics: or using the appearance delegate to set the background image for the navigation bar.

Why won't this UIButton display as my leftBarButtonItem, UIBarButtonItem seems to be set properly

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];

Resources