How do I prevent a button's background image from stretching?

45,235

Solution 1

A lot of people make the same mistake you do in regards to button images and then jump through hoops trying to make the button behave as they expect it to. Let's clear this up once and for all:

A UIButton has two types of images it can display -- a foreground image and a background image. The background image for a button is expected to replace the button's background texture. As such, it makes sense that it stretches to fill the entire background. However, the button's foreground image is expected to be an icon that may or may not display alongside text; it will not stretch. It may shrink if the frame is smaller than the image, but it will not stretch. You can even set the alignment of the foreground image using the Control alignment properties in Interface Builder.

A button's foreground and background image can be set in code like this:

// stretchy
[self setBackgroundImage:backgroundImage forState:UIControlStateNormal];  

// not stretchy
[self setImage:forgroundImage forState:UIControlStateNormal]; 

Solution 2

You don't have access to the background imageView, but there is fully working workaround:

EDIT: There is an even better workaround then what I posted originally. You can create a UIImage from any color, and call -setBackgroundImage:forState.

See bradley's answer, here: https://stackoverflow.com/a/20303841/1147286


Original answer:

Instead of calling -setBackgroundImage:forState:, create a new UIImageView and add it as a subview of the button.

UIImageView *bgImageView = [[UIImageView alloc] initWithImage:img];
bgImageView.contentMode = UIViewContentModeScaleAspectFit;
[bgImageView setFrame:CGRectMake(0, 0, videoButton.frame.size.width, videoButton.frame.size.height)];
bgImageView.tag = 99;
[yourButton addSubview:bgImageView];
[yourButton bringSubviewToFront:yourButton.imageView];
  1. Create the imageview
  2. Set the content mode and frame
  3. I also set a recognizable tag, so that when the screen rotates I can easily find my custom imageView in the button's subviews and reset its frame
  4. Add it as a subview to the button
  5. Bring the frontal imageView of the button to the front so our custom imageView doesn't overlap it

When the button needs to rotate just find the imageView by its tag and reset its frame:

UIImageView *bgImageView = (UIImageView *)[button viewWithTag:99];
[bgImageView setFrame:CGRectMake(0, 0, newWidth, newHeight)];

Solution 3

The cleanest and easiest way it probably to use the title insets of the button.

You set your image as the button image, and then you change the left title inset to match minus the width of your image:

myButton.titleEdgeInsets = UIEdgeInsetsMake(0, -myImage.width, 0, 0)

This will move the text back where it was before the image was added to its left. You can also use this value to add some padding to you button.

Solution 4

Stumbled on this problem too.

Adding image programmatically, as memmons thoroughly explained, did not help:(

I had a button 100x40 and image 100x100, it would appear squeezed, not fitted, as one would infer from "Aspect Fit" option. Actually, non of those view options had an effect.

I just had to rescale it so it would fit on a button, then use setImage:

UIImage *img=[UIImage imageNamed:@"myimage.png"];
CGImageRef imgRef = [img CGImage];
CGFloat imgW = CGImageGetWidth(imgRef);
CGFloat imgH = CGImageGetHeight(imgRef);
CGFloat btnW = myBttn.frame.size.width;
CGFloat btnH = myBttn.frame.size.height;
//get lesser button dimension
CGFloat minBtn=btnW;
if (btnW>btnH) {
    minBtn=btnH;
}
//calculate scale using greater image dimension
CGFloat scl=imgH/minBtn;
if (imgW>imgH) {
    scl=imgW/minBtn;
}
//scale image
UIImage *scaledImage = [UIImage imageWithCGImage:[img CGImage] scale:(img.scale * scl) orientation:(img.imageOrientation)];
//clean up
UIGraphicsEndImageContext();
//set it on a button
[myBttn setImage:scaledImage forState:UIControlStateNormal];

Solution 5

It is simple as:

    ImageBn.imageView?.contentMode = .scaleAspectFit
    ImageBn.setImage(chosenImage, for: .normal)
Share:
45,235
Raphael Caixeta
Author by

Raphael Caixeta

Web & iOS Developer. @gripd co-founder. #crossfitter. Adrenaline rush junkie. Brazilian. All around nerd, overall awesome.

Updated on March 04, 2021

Comments

  • Raphael Caixeta
    Raphael Caixeta about 3 years

    I'm allocating a UIButtonTypeCustom UIButton to a UIView with a background image that is smaller than the button's frame. Reason why the image is smaller is because I'm trying to add more of a "target area" for the UIButton. However, the image is being scaled to the full size of the frame, rather than just being the image's size.

    I have tried setting the UIButton and UIButton's imageView contentMode property to "UIViewContentModeScaleAspectFit", but no luck, the image still gets stretched out.

    Is there a way to do what I'm trying to do programmatically?

    Thanks in advance!