问题
I have a UIButton in my iPhone app. I set its size to 100x100.
I have an image that is 400x200 that I wish to display in the button.
The button STILL needs to stay at 100x100... and I want the image to downsize to fit... but keep the correct aspect ratio.
I thought that's what "Aspect Fit" was used for.
Do I include my image with setImage, or setBackgroundImage?
Do I set AspectFit for the button? or the image? or the background image?
(I need the smaller images to INCREASE in size. While larger images should DESCREASE in size. Always keeping the button at 100x100.)
I've tried countless combinations of all of the above... and I just can't seem to get everything to work at the same time:
- Don't change the button's size of 100x100.
- Don't destroy the image's aspect ratio.
- Always increase small images to fit the button.
- Always decrease large images to fit the button.
- Never clip any of the image edges.
- Never require the "put UIButtons over all your UIimages" hack.
- Don't require everyone to upgrade to v4.2.1 just to use new framework methods.
I see so many apps, with so many fully-working image-buttons... that I can't believe I can't figure out this very simple, very common thing.
Ugh.
回答1:
UIButton is broken. That's the short answer. The UIImageView
s in its image and backgroundImage properties don't respect UIViewContentMode
settings. They're read-only properties, and while the UIImage
contents of those UIImageViews can be set through setImage:
and setBackgroundImage:
methods, the content mode can't be.
The solution is either to provide properly-sized images in your bundle to begin with, or to put a UIImageView down, configure it the way you want it, and then put a clear "custom" UIButton over top of it. That's the hack all those fancy professional apps you've seen have used, I promise. We're all having to do it.
回答2:
UIImage *img = [UIImage imageNamed:@"yourImageName"];
button.imageView.contentMode = UIViewContentModeScaleAspectFit;
[button setImage:img forState:UIControlStateNormal];
回答3:
To do this correctly, I would actually programmatically resize and manipulate the image to get the desired aspect ratio. This avoids the need for any view hierarchy hacks, and also reduces any performance hit to a single operation, instead of every redraw.
This (untested) code should help illustrate what I mean:
CGSize imageSize = image.size;
CGFloat currentAspect = imageSize.width / imageSize.height;
// for purposes of illustration
CGFloat targetWidth = 100;
CGFloat targetHeight = 100;
CGFloat targetAspect = targetWidth / targetHeight;
CGFloat newWidth, newHeight;
if (currentAspect > targetAspect) {
// width will end up at 100, height needs to be smaller
newWidth = targetWidth;
newHeight = targetWidth / currentAspect;
} else {
// height will end up at 100, width needs to be smaller
newHeight = targetHeight;
newWidth = targetHeight * currentAspect;
}
size_t bytesPerPixel = 4;
// although the image will be resized to { newWidth, newHeight }, it needs
// to be padded with empty space to provide the aspect fit behavior
//
// use calloc() to clear the data as it's allocated
void *imageData = calloc(targetWidth * targetHeight, bytesPerPixel);
if (!imageData) {
// error out
return;
}
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
if (!colorSpace) {
// error out
return;
}
CGContextRef context = CGBitmapContextCreate(
imageData,
targetWidth,
targetHeight,
8, // bits per component
targetWidth * bytesPerPixel, // bytes per row
colorSpace,
kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst
);
CGColorSpaceRelease(colorSpace);
// now we have a context to draw the original image into
// in doing so, we want to center it, so prepare the geometry
CGRect drawRect = CGRectMake(
floor((targetWidth - newWidth) / 2),
floor((targetHeight - newHeight) / 2),
round(newWidth),
round(newHeight)
);
CGContextDrawImage(context, drawRect, image.CGImage);
// now that the bitmap context contains the aspect fit image with transparency
// letterboxing, we want to pull out a new image from it
CGImageRef newImage = CGBitmapContextCreateImage(context);
// destroy the temporary context
CGContextRelease(context);
free(imageData);
// and, finally, create a new UIImage
UIImage *newUIImage = [UIImage imageWithCGImage:newImage];
CGImageRelease(newImage);
Let me know if any part of that is unclear.
回答4:
Since none of my attempts have worked....
Maybe I should be asking this instead. When using a UIButton:
When DO I use setImage instead of setBackgroundImage? (Why are there both?)
When DO I use "Aspect Fit" instead of "Center"? (Why do both seem to stretch my images when I expect them to "keep aspect ratio" and "don't resize anything", respective.)
And the big question: Why is such a common thing... such a huge mess?
It would all be solved if I could find a work-around method like: Just use UIImage instead and detect TAPS. (But that seems to be even a LARGER nightmare of code.)
Apple, if you've tried to make my job easier... you have instead made it 400 times more confusing.
回答5:
I think what Dan is trying to say (but without ever saying it) is to do this:
- Use a "temp image" to do the resizing for you.
- The temp-image needs to be set to ASPECT FIT and HIDDEN.
- Make sure your button is set to your desired size, and NOT set to ASPECT FIT.
// Make a frame the same size as your button CGRect aFrame = CGRectMake(0, 0, myButton.frame.size.width, myButton.frame.size.height); // Set your temp-image to the size of your button imgTemp.frame = aFrame; // Put your image into the temp-image imgTemp.image = anImage; // Copy that resized temp-image to your button [myButton setBackgroundImage:tempImage forState:UIControlStateNormal];
回答6:
Place a imageview over the button, set your image for the imageview and not for button.
All the best.
回答7:
I would resize the image to 100x100 maintaining the aspect ratio of the content contained in the image. Then set the backgroundImage property of the UIButton to the image.
回答8:
I faced same issue few days back and resolved it. Please try with this
[_profilePicBtn setImage:profilePic forState:UIControlStateNormal];
_profilePicBtn.imageView.contentMode = UIViewContentModeScaleAspectFit;
来源:https://stackoverflow.com/questions/4502013/a-simple-way-to-put-a-uiimage-in-a-uibutton