setBackButtonBackgroundImage with weird appearance - ios

using this in AppDelegate.m for my custom NavBar:
UIImage *NavigationPortraitBackground = [[UIImage imageNamed:#"gradient_main"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0)];
[[UINavigationBar appearance] setBackgroundImage:NavigationPortraitBackground forBarMetrics:UIBarMetricsDefault];
UIImage *barBackBtnImg = [[UIImage imageNamed:#"btn_nav_default.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0)];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:barBackBtnImg forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
results in:
As you can see the button seems to be repeated. Button dimensions are 61x30. And the text is not centered.
If the text is shorter (e.g. Menu) the button image is cut off and if text is longer, then button is repeating.
Similar problem with buttons that have a smaller icon (30x30). The button shows fine but I can click in the button outside of the image :(

The root of your problem is that you are providing a resizeable image without UIEdgeInsets and without specifying the resizing style. The text on your UIBarButtonItem is actually centered! If you measure the distance on both sides of the text, you'll realize that it's the same.
If you look at the image you provide for the UIBarButtonItem:
[[UIImage imageNamed:#"btn_nav_default.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0)];
You're telling iOS that #"btn_nav_default.png" is a resizeable image and that it should tile. It's basically taking copies of your image and placing them side by side until the full width is covered. If you look at UIImage's Documentation you'll see that the default behaviour for resizableImageWithCapInsets is to tile. You will want to call resizableImageWithCapInsets:resizingMode: and pass in UIImageResizingModeStretch for your resizing mode.
That being said, that won't be enough. What you'll see after that is that the entire image is stretched, completely destroying your nice rounded corners. You need to provide UIEdgeInsets that tell UIImage that "you cannot stretch this section". In this case, providing UIEdgeInsets of UIEdgeInsetsMake(0.0f, 10.0f, 0.0f, 10.0f) (or something similar) will work.
If you're not interested in the long explanation, copy paste this code (no guarantees that this compiles though...):
[[UIImage imageNamed:#"btn_nav_default.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0.0f, 10.0f, 0.0f, 10.0f) resizingMode:UIImageResizingModeStretch];

You can try to play with
[[UIBarButtonItem appearance] setBackButtonBackgroundVerticalPositionAdjustment:5 forBarMetrics:UIBarMetricsDefault];
in ios 5 after you put the insets

Related

UIImage's resizableImageWithCapInsets places a shadow on image

I'm having trouble customising an UISegmentedControl: I've subclassed it I'm setting it's background for both the selected state and the unselected state like this:
#define kEdgeInsets UIEdgeInsetsMake(18, 18, 18, 18)
UIImage *grayImage = [[UIImage v_imageNamed:#"gray_rect"] resizableImageWithCapInsets:kEdgeInsets];
[self setBackgroundImage:grayImage
forState:UIControlStateNormal
barMetrics:UIBarMetricsDefault];
UIImage *greenImage = [[UIImage v_imageNamed:#"green_rect"] resizableImageWithCapInsets:kEdgeInsets];
[self setBackgroundImage:greenImage
forState:UIControlStateSelected
barMetrics:UIBarMetricsDefault];
[self setTintColor:[UIColor colorWithRed:0.506 green:0.514 blue:0.525 alpha:1.000]];
Where this are the PNGs I'm using
Now, when I execute this code, I get a weird shadow on the segmented control, which is not what we want. This is what the output looks like
Which is very weird, because there is no shadow on the original images, nor does UISegmentedControl add one (as far as I know).
Further checking, I noticed that if I removed the resizableImageWithCapInsets: call, the image looks distorted (as one should expect) but without the shadow.
Any ideas? because I'm literally going mad over this, since I don't have this problem when using resizableImageWithCapInsets: with UIButton
Thanks a lot!
I figured it out.
Turns out the segmented controller's frame is of 44pts height, and the background images was 75ptsx75pts. Since i've set the image's top and bottom inset to 18pts, the OS was taking the image's top and bottom 18pts and resizing it, ignoring the rest. Here is the fun part, since the image has a gradient, the rest of the image is ignored and it the control is colored like that.
In order to use images with vertical gradients you must use a base PNG with the exact same height as your control (http://useyourloaf.com/blog/2012/07/05/customizing-appearance-with-resizable-images.html)
This is what I did, resize the image to 44x44pts and change the insets to UIEdgeInsetsMake(0, 10, 0, 10) denoting that the image can't have to be resized vertically

Scaling down a UIButton's background image when using initWithFrame:

This is the first time I have ever designed an iOS app so I want to make sure I understand this behavior correctly.
I designed a custom bar button icon for a navigation bar in Photoshop. The final image that I saved in Photoshop was 102 x 45, and yes I realize that these dimensions are bigger than the recommended 44x44 in the iOS 7 design guidelines.
Anyways, I placed my image into the asset folder, and then programmatically set the bar button item with the following code:
UIImage* firstButtonImage = [UIImage imageNamed:#"loginbutton1"];
CGRect frame = CGRectMake(0, 0, 102, 45);
UIButton * someButton = [[UIButton alloc] initWithFrame:frame];
[someButton setBackgroundImage:firstButtonImage forState:UIControlStateNormal];
[someButton addTarget:self action:#selector(didTapLoginButton:)
forControlEvents:UIControlEventTouchUpInside];
self.rightBarButton = [[UIBarButtonItem alloc] initWithCustomView:someButton];
self.navItem.rightBarButtonItem = self.rightBarButton;
As you can see I set the frame's width and height to the exact size of the image. When I first ran the app, I didn't like the image and thought it was too big. So I changed the width and height parameters in this statement:
CGRect frame = CGRectMake(0, 0, 70, 30);
And now the image looks perfect on the iPhone screen. This is on an iPhone 4s.
So my main question is, what is actually happening when I change the frame size? Since the frame is now smaller than the actual image size, does the image just get scaled down automatically to fit inside the frame?
Yes the image get scaled because you are using backgroundImage (not Image). Both images have different behaviors.
Check the Xcode Interface Builder, you can see there, that you can set two images: Image and Background. Background is the UIImage that get scaled for the whole frame of the UIButton.
The UIButton Class Reference allows you to access the imageView of the image (not theimageView of the backgroundImage)
Because you have access to the imageView, you can change the mode of the image with:
[[someButton imageView] setContentMode:UIViewContentModeBottomLeft];
In UIView Class Reference you can check all the UIViewContentModes provided by Apple.
You can check that changing a little bit your code:
[someButton setImage:firstButtonImage forState:UIControlStateNormal];
[[someButton imageView] setContentMode:UIViewContentModeBottomRight];

UIImage PNG Opacity adjustment

I have created a custom button using a .png image that has transparency. However, when I implement it as a UIImage, the transparency is lost. Here's the code that I'm using:
- (void)setMyCustomBackButton;
{
UIImage *backButtonImage = [UIImage imageNamed:#"Back Button.png"];
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
[backButton setBackgroundImage:backButtonImage forState:UIControlStateNormal];
[backButton addTarget:self action:#selector(popCurrentViewController) forControlEvents:UIControlEventTouchUpInside];
backButton.frame = CGRectMake(0, 0, backButtonImage.size.width, backButtonImage.size.height);
UIView *backButtonView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 63, 33)];
backButtonView.bounds = CGRectOffset(backButtonView.bounds, -12, -2);
[backButtonView addSubview:backButton];
UIBarButtonItem *finalBackButton = [[UIBarButtonItem alloc] initWithCustomView:backButtonView];
self.navigationItem.leftBarButtonItem = finalBackButton;
}
Is there a UIImage property for transparency? Does it have to do with opacity?
If you're creating the PNG in an image editing software, please make sure you've encoded the transparency while creating the image. Sometimes some image editors have an option for 'saving transparency' that's unchecked by default, when you're exporting something as a PNG.
Other than that, any transparency on a PNG will show up on a UIButtonTypeCustom. You don't need to do anything special to preserve transparency on a UIImage that's loading up a PNG.
Before you use the image in the code, please open it in Preview to make sure the transparency is present.
Oh and to answer your other question, UIImage does not have any properties relating to transparency. The closest thing you have is the alpha property for a UIView but even that simply changes the overall opacity of your UIView.
EDIT: Missed this the first time I read your question. Try:
UIBarButtonItem *finalBackButton = [[UIBarButtonItem alloc] initWithCustomView:backButton];
No need to have the backButtonView. You can pass in the UIButton as the custom view. I use this to create custom bar button items. This should fix your problem.
EDIT 2: The UIBarButtonItem class does not have an alpha property you can set. Also, modifying the alpha property of the UIButton that you set as the UIBarButtonItem's custom view won't affect the transparency of the UIBarButtonItem.
The only way you can do this is to modify the transparency of the source image being used for the UIButton.
Modify your original PNG to have the desired transparency you want (and based on your comments, you need a very low transparency). Use an image editing software / preview to fine tune the transparency before you export the PNG.

How do I an image at a specific location in a navigation bar?

Im customizing my navbars so they all have a logo in them. I want to add the logo right around here:
I know navbars have a background image property but that would stretch it to the whole navbar. I just want it there! :)
So far Ive tried this:
UIImage *gradientImage44 = [[UIImage imageNamed:#"NavBar25High"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0)];
UIImage *gradientImage32 = [[UIImage imageNamed:#"NavBar25High"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0)];
// Set the background image for *all* UINavigationBars
[[UINavigationBar appearance] setBackgroundImage:gradientImage44 forBarMetrics:UIBarMetricsDefault];
[[UINavigationBar appearance] setBackgroundImage:gradientImage32 forBarMetrics:UIBarMetricsLandscapePhone];
But thats for gradients because as I mentioned, it uses the image as a background image :(
I would allocate an ImageView using CGRectMake to the specific location you want it and add that image view to the view of the UINavigationController.
Like this:
Declare the image
UIImage *myImage =[UIImage imageNamed:#"header.png"];
Allocate the image view
myImageView_tools = [[UIImageView alloc] initWithFrame:CGRectMake(97.5, 20, 125, 45)];
Set the image as the image of the imageview
myImageView_tools.image = myImage;
Add this to your navbar.
[nameofyournavbar.view addSubview:myImageView_tools];
In my code I happened to add it to the RootViewController instead of the NavigationBar but I would imagine it would work just the same.
How about adding the image as the custom view of a UIBarButtonItem. Maybe using a UIBarButtonSystemItemFixedSpace item with a specified width.

UIEdgeInsets resizing Back button

I'm trying to customize the back button of a UINavigationBar using the iOS 5 UIAppearance API. The image I want to use is this: https://www.dropbox.com/s/ce83rw0e3vs9dwo/bt-back.png and the code is the following:
// Customize back button items differently
UIEdgeInsets aInset = UIEdgeInsetsMake(10, 10, 10, 10);
UIImage *buttonBack30 = [[UIImage imageNamed:#"bt-back.png"] resizableImageWithCapInsets:aInset];
UIImage *buttonBack24 = [[UIImage imageNamed:#"bt-back.png"] resizableImageWithCapInsets:aInset];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:buttonBack30 forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:buttonBack24 forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone];
Now, the output of that code is the following: http://i.imgur.com/X6QBK.png
I know I have to set the UIEdgeInsets to a proper value, but I've being reading the documentation and it seems that it's only possible to preserve the edges and not the center, witch seems to be the thing I need to do.
Is there any way to preserve the center and not the edges? If not, what's the dimensions I have to generate the PNG so iOS doesn't stretch it?
Thanks a lot
Could you use the method
- (UIImage *)backButtonBackgroundImageForState:(UIControlState)state
barMetrics:(UIBarMetrics)barMetrics
to retrieve the back button's background image, retrieve its size property, and then use this for the size to make your custom image?

Resources