scale Image in an UIButton to AspectFit?

122,734

Solution 1

If you really want to scale an image, do it, but you should resize it before using it. Resizing it at run time will just lose CPU cycles.

This is the category I'm using to scale an image :

UIImage+Extra.h

@interface UIImage (Extras)
- (UIImage *)imageByScalingProportionallyToSize:(CGSize)targetSize;
@end;

UIImage+Extra.m

@implementation UIImage (Extras)

- (UIImage *)imageByScalingProportionallyToSize:(CGSize)targetSize {

UIImage *sourceImage = self;
UIImage *newImage = nil;

CGSize imageSize = sourceImage.size;
CGFloat width = imageSize.width;
CGFloat height = imageSize.height;

CGFloat targetWidth = targetSize.width;
CGFloat targetHeight = targetSize.height;

CGFloat scaleFactor = 0.0;
CGFloat scaledWidth = targetWidth;
CGFloat scaledHeight = targetHeight;

CGPoint thumbnailPoint = CGPointMake(0.0,0.0);

if (!CGSizeEqualToSize(imageSize, targetSize)) {

        CGFloat widthFactor = targetWidth / width;
        CGFloat heightFactor = targetHeight / height;

        if (widthFactor < heightFactor) 
                scaleFactor = widthFactor;
        else
                scaleFactor = heightFactor;

        scaledWidth  = width * scaleFactor;
        scaledHeight = height * scaleFactor;

        // center the image

        if (widthFactor < heightFactor) {
                thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5; 
        } else if (widthFactor > heightFactor) {
                thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5;
        }
}


// this is actually the interesting part:

UIGraphicsBeginImageContextWithOptions(targetSize, NO, 0);

CGRect thumbnailRect = CGRectZero;
thumbnailRect.origin = thumbnailPoint;
thumbnailRect.size.width  = scaledWidth;
thumbnailRect.size.height = scaledHeight;

[sourceImage drawInRect:thumbnailRect];

newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

if(newImage == nil) NSLog(@"could not scale image");


return newImage ;
}

@end

You can use it to the size you want. Like :

[self.itemImageButton setImage:[stretchImage imageByScalingProportionallyToSize:CGSizeMake(20,20)]];

Solution 2

I had the same problem. Just set the ContentMode of the ImageView that is inside the UIButton.

[[self.itemImageButton imageView] setContentMode: UIViewContentModeScaleAspectFit];
[self.itemImageButton setImage:[UIImage imageNamed:stretchImage] forState:UIControlStateNormal];

Hope this helps.

Solution 3

None of the answers here really worked for me, I solved the problem with the following code:

button.contentMode = UIViewContentModeScaleToFill;
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentFill;
button.contentVerticalAlignment = UIControlContentVerticalAlignmentFill;

You can do this in the Interface Builder as well.

enter image description here

Solution 4

The easiest way to programmatically set a UIButton imageView in aspect fit mode :

Swift

button.contentHorizontalAlignment = .fill
button.contentVerticalAlignment = .fill
button.imageView?.contentMode = .scaleAspectFit

Objective-C

button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentFill;
button.contentVerticalAlignment = UIControlContentVerticalAlignmentFill;
button.imageView.contentMode = UIViewContentModeScaleAspectFit;

Note: You can change .scaleAspectFit (UIViewContentModeScaleAspectFit) to .scaleAspectFill (UIViewContentModeScaleAspectFill) to set an aspect fill mode

Solution 5

I had problems with the image not resizing proportionately so the way I fixed it was using edge insets.

fooButton.contentEdgeInsets = UIEdgeInsetsMake(10, 15, 10, 15);
Share:
122,734

Related videos on Youtube

KONG
Author by

KONG

Learning is my primary desire!

Updated on September 24, 2020

Comments

  • KONG
    KONG over 3 years

    I want to add an image to a UIButton, and also want to scale my image to fit with the UIButton (make image smaller). Please show me how to do it.

    This is what I have tried, but it does't work:

    • Adding image to button and using setContentMode:
    [self.itemImageButton setImage:stretchImage forState:UIControlStateNormal];
    [self.itemImageButton setContentMode:UIViewContentModeScaleAspectFit];
    
    • Making a "stretch image":
    UIImage *stretchImage = [updatedItem.thumbnail stretchableImageWithLeftCapWidth:0 topCapHeight:0];
    
    • Shaun Budhram
      Shaun Budhram almost 13 years
      I think this has been changed to be the default behavior in firmware 4.0 and up.
    • Matt
      Matt over 9 years
      See stackoverflow.com/a/28205566/481207 for the solution.
  • KONG
    KONG over 14 years
    Thanks for your answer, Gcamp. I did have an method for resizing by ratio. But I still try to find a way to tell UIButton do it for me. My image is load from Internet so I can't resize it at compile time. BTW, thank you very much for your response.
  • Alexandre Gomes
    Alexandre Gomes about 13 years
    Make sure you're setting the imageView. I also failed to notice, for a bit, that I was still using the backgroundImageView and it wasn't working for that.
  • Ben Gotow
    Ben Gotow over 12 years
    Because the image is placed into a CALayer, rendered, and cached by Quartz, performance should not be any worse if you place it in there at full size. Either way, you're resizing ~1 time. gcamp's answer is much more important if you're constantly resizing the button or doing other things that would trigger Quartz to redraw the image layer.
  • Yuanfei Zhu
    Yuanfei Zhu about 12 years
    It seems this method has some problems when the button state is 'highlighted'. The image will back to the fill mode. Any idea?
  • Mickey
    Mickey over 11 years
    It is not working to me. However, I make it work by using [self.itemImageButton setbackgroundImage:[UIImage imageNamed:stretchImage] forState:UIControlStateNormal];
  • Christopher
    Christopher over 11 years
    This worked for me. I set the content mode as above. However, you also need to set the same image of the highlighted state in order to avoid the image going back to "fill mode". For example, [imageButton setImage:image forState:UIControlStateHighlighted];
  • onmyway133
    onmyway133 over 10 years
    @Dave from Apple doc about imageView The button’s image view. (read-only) that's why it does not work
  • manecosta
    manecosta almost 10 years
    You're not setting a content mode, you're setting the image. And they're not events, they're states. You're making a big mess. This is totally unrelated to the question.
  • Andy
    Andy about 9 years
    This worked for me -- but only if I selected to use the "Background Image", not the actual image. Whatever works! This was driving me NUTS.
  • Khant Thu Linn
    Khant Thu Linn about 9 years
    it seems like image quality is reduced. i am testing on iphone 6 plus. Is it true? I also have 3x image.
  • gcamp
    gcamp about 9 years
    Yes it will, this code is pretty old. It was UIGraphicsBeginImageContext instead of UIGraphicsBeginImageContextWithOptions. Just fixed it.
  • RoLYroLLs
    RoLYroLLs about 9 years
    Under Xcode 6.2 this worked for me but like @AndrewHeinlein said, used Background Image
  • bendytree
    bendytree about 9 years
    You can do this in Interface Builder with key path imageView.contentMode, type Number, and value 1
  • sdsykes
    sdsykes almost 9 years
    That should be yourButton.imageView.contentMode = UIViewContentModeScaleAspectFit;
  • Tulleb
    Tulleb almost 9 years
    Didn't try with .imageView. It is working without pretty well.
  • SHS
    SHS over 8 years
    apple help for how to do "bendytree" comment process in Interface Builder - developer.apple.com/library/mac/recipes/…
  • Radu Simionescu
    Radu Simionescu over 8 years
    yeah but in many cases it doesn't matter if you loose a few cycles, so no point in filling up the ram
  • Ortwin Gentz
    Ortwin Gentz about 8 years
    This doesn't give you aspect fit behavior as asked by the OP.
  • Daniel
    Daniel about 8 years
    @OrtwinGentz you can select the mode and set Aspect fit instead of scale to fill.
  • Iulian Onofrei
    Iulian Onofrei over 7 years
    Isn't this possible without a subclass?
  • Andy Poes
    Andy Poes over 7 years
    This answer was specifically for backgroundImage. For the main image you can monkey with the the imageEdgeInsets.
  • Iulian Onofrei
    Iulian Onofrei over 7 years
    I know, and I wanted if it's possible to achieve this without a subclass.
  • Andy Poes
    Andy Poes over 7 years
    As far as I know, you cannot adjust the backgroundImage without subclassing.
  • BharathRao
    BharathRao almost 5 years
    Nice answer bro..helped me a lot. Thanks!