UIImage's resizableImageWithCapInsets places a shadow on image - ios

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

Related

IOS Button border thickness lssue,why?

UIButton top border appears thicker than the following ,but sometimes correct ,why?
code:
UIImage * sanImage = [UIimage imageNamed:#"product_bt1_normal"];
[self.saveBtn setBackgroundImage:[sanImage
stretchableImageWithLeftCapWidth:sanImage.size.width/3
topCapHeight:sanImage.size.height/3] forState:UIControlStateNormal];
Are you trying to make a button? If so, perhaps use a UIButton instead? You can control the border with button.layer.borderWidth = 1.0f
If you're set on using an image, create a UIImageView, and modify the border thickness that way:
UIImageView *iv = [[UIImageView alloc] initWithImage:sanImage];
[iv.layer setBorderWidth:0.5f];
It could be because of off-pixel boundaries. Since you are using height/3.0f, your image is maybe not returning a well-behaved image.
Also, there is a new stretchable image method you should be using, resizableImageWithCapInsets:.
So try this code out:
[self.saveBtn setBackgroundImage:[sanImage resizableImageWithCapInsets:UIEdgeInsetsMake(3.0f, 3.0f, 3.0f, 3.0f)] forState:UIControlStateNormal];
You might need to mess with the values for the insets a bit, I don't know the dimensions of your button image.

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

Building a UIButton out of 3 images

I want to build a UIButton out of 3 images (A,B,C). A and C are stretchable components (depending on the button text) the middle part is fixed.
What is the best way to attach these images to one another? How can I be flexible in the implementation without specifying all origins and widths and so on... Is it necessary to have a surrounding rectangle?
You need to stitch your images together in an image editor to create a single image. Then you'll load this image, and create a resizable version using the UIImage resizableImageWithCapInsets:resizingMode: method. You'll specify UIEdgeInsets that mirror the original widths of your two "end cap" images.
For the button itself you need to make this resizable image the background image of the button. It won't work as a foreground image.
UIImage* bgImage = [[UIImage imageNamed:#"my_bg_image"] resizableImageWithCapInsets: UIEdgeInsetsMake(0, 10, 0, 10)];
UIButton* b = [UIButton buttonWithType: UIButtonTypeCustom];
[b setBackgroundImage: bgImage forState: UIControlStateNormal];
I've had to implement something like this recently.
My strategy was to build what I wanted using UIView objects, and then write the created view to a UIImage.
I'd recommend:-create a container view-create UILabels for the button text, add to container view-for labels A and C, sizeToFit them after they have their text, and then use that information to place the stretchable buttons in the view-once the subviews are positioned, you'll probably want to reset the frame of your container view to make sure it is still correct.
once your view looks the way you want it, write it to an image using:
UIGraphicsBeginImageContextWithOptions(container.frame.size, NO, 0); //or YES if you have no opacity in your images
[container.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
-you can then set this as the image for your UIButton
The best way to do this is to put the 3 images into one file, and use resizableImageWithCapInsets:. See the doco here.
Here's a good tutorial on how to do this.
// Set the button background image
UIImage * buttonImage = [UIImage imageNamed:#"button_resizable.png"];
UIImage * resizableButtonImage = [buttonImage resizableImageWithCapInsets:UIEdgeInsetsMake(0, 12, 0, 12)];
[button setBackgroundImage:resizableButtonImage forState:UIControlStateNormal];

setBackButtonBackgroundImage with weird appearance

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

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