I've had that problem before. I solved it by putting my image in a UIImageView, where contentMode settings actually work, and putting a transparent custom UIButton over top of that.
EDIT: This answer is obsolete. See @Werner Altewischer's answer for the correct answer in modern versions of iOS.
I had the same issue, but I couldn't get it to work (perhaps it's a bug with the SDK).
Eventually, as a workaround, I placed a UIImageView behind my button and set the options I wanted on that, then simply placed a blank UIButton on top of it.
UIView content modes apply to the corresponding CALayer's "content". This works for UIImageViews because they set the CALayer content to the corresponding CGImage.
drawRect: ultimately renders to the layer content.
A custom UIButton (as far as I know) has no content (the rounded-rect style buttons might be rendered using content). The button has subviews: the background UIImageView, the image UIImageView, and the title UILabel. Setting the contentMode on the subviews may do what you want, but messing around with the UIButton view hierarchy is a bit of a no-no.
The solution is to set the contentMode on the imageView property of the UIButton. The UIButton has to be created with custom type for this to work I believe (otherwise nil is returned for this property).
If you put the image in an UIImageView behind the button, you'll loose the built-in functionality of the UIButton class, such as adjustsImageWhenHighlighted and adjustsImageWhenDisabled, and of course the ability to set different images for different states (without the hazzle of doing this yourself).
If we want to have an image unstreched for all control states, one approuch is to get the image using imageWithCGImage:scale:orientation, as in the following method:
- (UIImage *) getScaledImage:(UIImage *)img insideButton:(UIButton *)btn {
// Check which dimension (width or height) to pay respect to and
// calculate the scale factor
CGFloat imgRatio = img.size.width / img.size.height,
btnRatio = btn.frame.size.width / btn.frame.size.height,
scaleFactor = (imgRatio > btnRatio
? img.size.width / btn.frame.size.width
: img.size.height / btn.frame.size.height;
// Create image using scale factor
UIImage *scaledImg = [UIImage imageWithCGImage:[img CGImage]
scale:scaleFactor
orientation:UIImageOrientationUp];
return scaledImg;
}
This should prevent the image from stretching in all control states. It worked for me, but let me know if it doesn't!
NOTE: Here we are addressing a problem relating to UIButton, but the insideButton: might as well be insideView:, or whatever one would like to fit the image into.
I had this problem a while back. The issue I had was i was trying to apply this effect to the background UIButton which is limited and therefore means you cannot adjust it as easy.
The trick is to set it as just an image then apply @ayreguitar's technique and that should fix it!
Changing UIButton.imageView.contentMode does not worked for me. I solved the problem by setting the image to 'Background' property.
You can add ratio constraint if you need
This overlaps many of the other answers, but the solution for me was to
set the contentMode of the UIImageView for the button to .ScaleAspectFit – which can either be done in the ”User Defined Runtime Attributes” in Interface Builder (ie. self.imageView.contentMode, Number, 1) or in a UIButton subclass;
disable ”Autoresize Subviews”;
set ”Edge” to ”Image” and appropriate ”Top” and ”Bottom” values for ”Inset” (which might only be needed if you, like me, used a PDF as image).
Combining together a few different answers into one solution-- create a button with a custom type, set the button's imageView contentMode property, and set the image for the button (not the background image, which will still scale to fill).