setting image for UIBarButtonItem - image stretched - ios

When I try to use UIBarButtonItem's "initWithImage" to initialize a navigation bar custom image, it comes out washed-up and stretched against a black navigation bar. This is how I create it:
UIBarButtonItem *button = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"gear.png"] style:UIBarButtonItemStyleBordered target:self action:#selector(showSetting:)];
Here is what it looks like:
Any idea if it's a problem with the image? I got it from a set of icons I bought.

The best way to do this is to create a button, set its background image, and set its action. Then a UIBarButtonItem can be created using this button as the custom view. Here's my example code:
UIButton *settingsView = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 61, 30)];
[settingsView addTarget:self action:#selector(SettingsClicked) forControlEvents:UIControlEventTouchUpInside];
[settingsView setBackgroundImage:[UIImage imageNamed:#"settings"] forState:UIControlStateNormal];
UIBarButtonItem *settingsButton = [[UIBarButtonItem alloc] initWithCustomView:settingsView];
[self.navigationItem setRightBarButtonItem:settingsButton];

The displayed images on bar button items are 'derived' from the source image (it uses only the alpha channel values in rendering but that all looks ok in your image). Its possibly just not the right size - you might need to open the image file and crop it to the right size.
You could also try looking at whether setting the imageInsets property (inherited by UIBarButtonItem from UIBarItem) can be used to adjust the size in a way to stop it getting stretched.
Doco on the bar item images says the following:
The images displayed on the bar are derived from this image. If this image is too large to fit on the bar, it is scaled to fit. Typically, the size of a toolbar and navigation bar image is 20 x 20 points.

I know this question already has a checkmarked anser. But I ran in to this today and thought I would offer up my answer anyway. The check marked answer above did help me, but it also took some extra experimenting to figure out what was really happening.
The button image is being shrunk only in the x axis and not the y. this is because it is too tall for the button and it shrinks it down to fit. But it doesn't shrink it proportionally. Only on the vertical. So it appears stretched. It's not actually stretched - which implies a widening of it. Instead the height is shrunk. Knowing the difference I think is important to understanding why it's happening and how to fix it.
I did the same thing the OP did. Thinking that I am supporting retina I made my icon 40x40. Mine was a green checkmark with an alpha channel. It was padded with blank pixels to be 40x40. The app resized it to fit within the button's available height. But the width stayed the same. So it became somewhere in the range of 40x30 or 40x20. I think the button can handle an icon 30 high, but then it's a little too big for the box IMHO.
The OP reduced the button to 30x30 and that made it not squish any more. But that's not the best solution. Because it isn't actually a retina button when you do that. It's shrunk and then blown back up on the retina.
The correct answer is to name your 40 pixel tall version with the #2x and then make a half size (20 pixel tall) version and save it without the #2x. The width can be whatever. Then load with imageNamed: without specifying the #2x. It will use the appropriate png for a retina or non-retina device.
The next thing that happened to me was then the button frame was too small. So I upped my canvas size in psd to pad the png to 80 wide to make the button slightly wider and more tappable.

I got the same stretched issue for my 40x40 image when I set the background image for leftBarButtonItem
UIBarButtonItem *backButton = [UIBarButtonItem new];
[backButton setBackButtonBackgroundImage:[UIImage imageNamed:#"back_icon"] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
self.navigationItem.leftBarButtonItem = backButton;
But my issue got resolved with the following code
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"back_icon"] style:UIBarButtonItemStylePlain target:self action:#selector(handleBack:)];
self.navigationItem.leftBarButtonItem = backButton;
and same result if UIBarButtonItemStyleBordered is used.

Set correct image size: #1x = 22px, #2x = 44px #3x = 88px firstly.
Then
let leftBarButtonItem = UIBarButtonItem(image: yourUIImage, style: .plain, target: self, action: #selector(action))
leftBarButtonItem.tintColor = UIColor.red
navigationItem.leftBarButtonItem = leftBarButtonItem
or
let btn = UIButton(type: .custom)
btn.addTarget(self, action: #selector(contactMe), for: .touchUpInside)
btn.setImage(#imageLiteral(resourceName: "open"), for: .normal)

For those who have come across this toolbar item stretching issue in iOS 11 specifically, it appears that the #2x version of your image is now required to render its frame and or bounds.
So if you have code like this where you're adding a custom image UIBarButtonItem like this:
UIButton *tagsBtn = [UIButton buttonWithType:UIButtonTypeCustom];
tagsBtn.bounds = CGRectMake( 0, 0, 40, 40);
[tagsBtn setImage:[UIImage imageNamed:#"tags.png"] forState:UIControlStateNormal];
tags = [[UIBarButtonItem alloc] initWithCustomView:tagsBtn];
[tagsBtn addTarget:self action:#selector(tags:) forControlEvents:UIControlEventTouchUpInside];
[bottomToolbar setItems:[NSArray arrayWithObjects:flexibleSpace,tags,flexibleSpace,nil]];
Then you will need to have a tags#2x.png that's 80x80, even if your tags.png image is 80x80. Simply renaming tags.png to tags#2x.png would resize the image to 40x40 as it did pre iOS 11 without changing code, or just add tags#2x.png to your project.

Related

Navigation bar title is not centred in iPhone's portrait mode

I have created a ViewController Using Xib file of size wAny and hAny and i have used autolayaout and constraint to fit UI all devices and all orientation.
Everything is working fine but when i'm running app on iPhone portrait mode title of navigation bar is not centred where as when rotating it to landscape mode title is centred.
I have used following code for setting title
self.title = #"Download Schedule";
Already tried:
I have tried to set custom UILabel of lesser font size to Navigation bar's title view but still i'm getting it on left side .
Here is code of my right button
//add right bar button for more options
UIView *navBarButtonsView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 44, 44)];
UIBarButtonItem *item0 = [UIBarButtonItem negativeSpacerWithWidth:5];
UIButton *moreButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 44, 44)];
[moreButton setBackgroundImage:[UIImage imageNamed:#"ic_web"] forState:UIControlStateNormal];
[moreButton addTarget:self action:#selector(addNewSchedule) forControlEvents:UIControlEventTouchDown];
[navBarButtonsView addSubview:moreButton];
UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithCustomView:navBarButtonsView];
self.navigationItem.rightBarButtonItems = [NSArray arrayWithObjects:item0, backButton, nil];
There is something else going on - something you have not told us about. I suspect that there is something wrong with how you are setting your rightButtonItems, such that there are invisible buttons taking up the space on the right. I can reproduce this issue only by deliberately making the right button really wide:
So I suspect you have applied a large width to the right button, or there are extra invisible right button items taking up the space on the right.

How to change UIBarButtonItem size programmatically?

UIBarButtonItem *adminBarButtonItem = [[UIBarButtonItem alloc]
initWithImage:[UIImage imageNamed:#"779-users"]
style:UIBarButtonItemStylePlain
target:self
action:#selector(adminButtonTouched)];
I tried to assign a new frame to adminBarButtonItem.customView.frame, it didn't work.
Expect for [[UIBarButtonItem alloc] initWithCustomView:aView], is there anyway to change the size of UIBarButtonItem?
I encountered a similar situation where I needed to resize a custom image to fit alongside another system bar button item. The key is to resize the image itself before I use it in UIBarButtonItem's initWithImage method.
For how to resize that UIImage, follow this link The simplest way to resize an UIImage?
Another pitfall is the tintColor of the custom button item. I used
myButtonItem.tintColor = [UIColor colorWithPatternImage:myImage];
to get around the blue tint that covers up my image (iOS8), but I suspect there exists a more kosher way. If anybody knows, please enlighten me.

Bar Button Item not correctly aligned

I have an issue with my navigation bar, the UIBarButtonItem is not correctly vertically aligned. I don't know why because I'm using a simple UIBarButtonItem and not a custom view. See my code below and thanks for your help!
UIBarButtonItem *newGameItem = [[UIBarButtonItem alloc] initWithTitle:#"New Game" style:UIBarButtonItemStyleDone target:self action:#selector(newGame)];
self.navigationItem.rightBarButtonItem = newGameItem;
Actually there are couple ways to align the bar buttons.
Try to adjust the button title position with this:
[self.navigationItem.rightBarButtonItem setTitlePositionAdjustment:UIOffsetMake(-10, -10)
forBarMetrics:UIBarMetricsDefault];
-10 -10 - just for example
OR
you can initialize your newGameItem with custom UIButton. The custom button is a subclass of UIButton with this method overridden
- (UIEdgeInsets)alignmentRectInsets {
return UIEdgeInsetsMake(0, -10, 0, 15.0f); // here you can play with values to align your button properly
}
then initialize your newGameItem
CustomBarButton *button = [CustomBarButton buttonWithType:UIButtonTypeCustom];
[button setTitle:#"New game" forState:UIControlStateNormal];
button.frame = CGRectMake (0, 0, 60, 40);
[button addTarget:self action:#selector(newGame) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *newGameItem = [[UIBarButtonItem alloc] initWithCustomView:button];
self.navigationItem.rightBarButtonItem = newGameItem;
I had a similar issue: We use custom fonts via UIAppearance for both navigation titles and bar button items, and in some cases, the right item was vertically misaligned.
I could easily fix the misalignment by using [self.navigationItem setRightBarButtonItem:rightItem animated:YES] instead of the property setter.
I had the same problem with a button on the left, I was able to fix it in Xcode 6.3.1: select the button (bar button item) right column tab "ruler" inset image Top value: -54 instead of 0.
Note that: my device is an iPhone4 with iOS 7, the button was perfectly in place in Xcode and misplaced on the device, now it is in place on the device and misplaced in Xcode
Unfortunately, there is not much you can do about UIBarButtonItem vertical spacing. I believe you'll have to either increase the font size of your title on the UINavigationBar (you can do this with UIAppearance also), or decrease the font size of your UIBarButtonItem.

Center custom TitleView containing an UIImage and UILabel

I checked all the other answers, but I cannot get it to work using what they say:
I have a UIView myTitle, with Frame (0,0,320,44) which contains an UIImage and a UILabel. The UIImage and the UILabel are fine and look fine, but I cannot seem to figure out how to center myTitle.
self.navigationItem.titleView = myTitle;
It shows up and looks ok, but much too far to the left. It seems to (logically) be related to the left bar button (the back button) which is auto generated by the framework when I push a controller. That barbutton has a varying width based on that and it changes the x of myTitle view based on that.
So, where can I get the width of the left (and right) barbuttons so I can figure out how to put myTitle that in the center?
I notice when I change the width of myTitle it (logically) will center (because the UIImage and UILabel are centered within myTitle).
To have more control, instead of having the left bar button auto -generated, set it using code as below.
UIButton *leftButton =[UIButton buttonWithType:UIButtonTypeCustom];
[leftButton setFrame:CGRectMake(0, 0, 100, 44)];
[leftButton setTitle:#"back" forState:UIControlStateNormal];
[leftButton setBackgroundColor:[UIColor greenColor]];
[leftButton addTarget:self action:#selector(backButtonClicked) forControlEvents:UIControlEventTouchUpInside];
self.navigationItem.leftBarButtonItem =[[UIBarButtonItem alloc] initWithCustomView:leftButton];
Now you can perfectly adjust the frame of titleView with out much problem.
I found two ways in which it works;
Like Cy-4AH suggested, using autolayout; her/his answer would be the answer
Not using autolayout it was much simpler than I thought; if i make the titleView Width to fit it's content exactly, it automatically works; is that done with auto layout internally?
Hope this helps someone.

UINavigationBar back button with pattern image

I need to create a navBar back button in accordance to the designer's plan. The back button has a pattern image and stitched leather on the perimeter.
Here it is:
My question is it possible to create this without a great amount of hassle and headache? Or if it's possible at all, since the back button has varying width?
Thanks!
UPDATE
Alright, with the help of PartiallyFinite turns out this is very easy. If you set the UIEdgeInsets correctly it will keep the left side fixed, the right side fixed, and then duplicate the middle of the image considering the back button's width.
This is the image I used for my back Button:
And these are my inset settings. You can try them yourself:
backButtonImage = [backButtonImage resizableImageWithCapInsets:UIEdgeInsetsMake(5, 17, 5, 12)];
Hope this helps someone in the future.
You will need to provide a stretchable image for the button, so it knows how to display it correctly:
UIImage *buttonImage = [[UIImage imageNamed:#"backButtonImage"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 15, 0, 6)]
You don't need to do anything special to the image itself, but you do need to specify appropriate edge insets for the resizable image to indicate the area around the edges of the image that should not be stretched, as shown above (the example shows an inset of 15 pixels from the left and 6 from the right). This area should cover the arrow head, and the curved right edge, so that the middle area can be stretched out as needed. Read the documentation for more information on this method.
UPDATE: By default, the resizable area of the image will be tiled to the new size, however if you want to have it stretch instead, you can use resizableImageWithCapInsets:resizingMode: and pass UIImageResizingModeStretch to achieve that behaviour. For your case obviously tiling is better as it preserves the stitching, but for some background images stretching is a better solution. Just putting this here to help anyone who sees this in the future.
Once you have the stretchable image, you can change the appearance your back button using this code:
[myBackButtonItem setBackButtonBackgroundImage:buttonImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
Alternatively, you can set this custom appearance for all back buttons in your app using this code:
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:buttonImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
Run this when your app launches, and it will affect all back buttons in your app.
Note that, contrary to what some of the other answers suggest, you will not need to manually create any back buttons. If you create a UINavigationController and use it in the recommended way (read about that in the documentation, a navigation bar and back button will be created for you as you push view controllers using pushViewController:animated:. If you use the global UIAppearance code snippet to apply the custom button style, it will automatically be applied to all the back buttons that you have.
You can read more about setBackButtonBackgroundImage:forState:barMetrics: in the official documentation.
There are also numerous tutorials available online for a more in-depth explanation of how this works and how to do it, here are a few good ones:
http://useyourloaf.com/blog/2012/08/24/using-appearance-proxy-to-style-apps.html
http://nshipster.com/uiappearance/
You need to create your own BarButtonItem. You can't set image for system back button.
self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc] initWithCustomView:buttonWithYourImage] autorelease];
You can create your own UIBarButtonItem and set it as the leftButtonItem on the navigation bar on the current view controller:
UIBarButtonItem *btn = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:#"your_image"] style:UIBarButtonItemStyleBordered target:self action:#selector(yourFunction:)];
self.navigationItem.leftBarButtonItem = btn;
I use this in my Appdelegate.m to customize button. ("buttonBack" is the name of your image). Hope it may help you
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
UIImage *barButtonImage = [[UIImage imageNamed:#"buttonBack"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 6, 0, 6)];
[[UIBarButtonItem appearance] setBackgroundImage:barButtonImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
return YES;
}
First you need to make your own back button.. and set that image to back button.
UIBarButtonItem * btn=[[UIBarButtonItem alloc]initWithTitle:#"Back" style:UIBarButtonItemStyleBordered target:self action:#selector(changed)];
self.navigationItem.leftBarButtonItem=btn;
[btn setImage:[UIImage imageNamed:#"yourimagename"]];
-(void)changed
{
[self.navigationController popToViewController:[[self.navigationController viewControllers] objectAtIndex:0] animated:YES];
}
try this one really helpful to you...

Resources