UIButton doesn't listen to content mode setting?
Asked Answered
O

17

99

firstButton is a UIButton of type Custom. I'm programmatically putting three of them across each cell of a table, thusly:

[firstButton setImage:markImage forState:UIControlStateNormal];
[firstButton setContentMode:UIViewContentModeScaleAspectFit];
[cell.contentView addSubview:firstButton];

Elsewhere, I'm telling it to clipToBounds. What I get is a crop of the center square of the image, rather than an aspect-scaled rendering of it. I've tried this lots of ways, including setting the mode property on firstButton.imageView, which also doesn't seem to work.

Oat answered 1/6, 2010 at 14:29 Comment(2)
See solution by Chris Nolet below: button.contentVerticalAlignment = UIControlContentVerticalAlignmentTop;Embrace
See https://mcmap.net/q/130503/-ios-gt-gt-uibutton-imageview-property-gt-gt-how-to-set-content-mode which shows that setting both contentVerticalAlignment and contentHorizontalAlignment is required for UIViewContentModeScaleAspectFit.Embrace
R
188

I had the same problem. I see this question is a little old, but I want to provide a clear and correct answer to save other folks (like me) some time when it pops up in their search results.

It took me a bit of searching and experimenting, but I found the solution. Simply set the ContentMode of the "hidden" ImageView that is inside the UIButton.

[[firstButton imageView] setContentMode: UIViewContentModeScaleAspectFit];
[firstButton setImage:[UIImage imageNamed:imageName] forState:UIControlStateNormal];

Perhaps that's what Dan Ray was alluding to in his accepted answer, but I suspect not.

Roselani answered 19/7, 2010 at 13:14 Comment(10)
No, it wasn't. The solution I settled on was to use a UIImageView to hold the image, then a clear, transparant UIButton of type "custom" on top of it.Oat
Dave, on iOS 3.2 at least, your solution doesn't seem to work for me. Too bad, because I was hoping to get the highlighting of the image on click "for free". I even made sure that button.imageView wasn't nil where I was setting contentMode, and set the image after setting contentMode, just like you're doing.Upholstery
Just to clarify, "doesn't work" means "the image is shown at its full resolution, not scaled down as I was hoping".Upholstery
Jacques, the above works for me in iOS 4.x. Unfortunately, I can reproduce the "won't resize" issue when building with a v3.2 target. I tried changing the UIButton view's frame size, but that didn't seem to help either. ugh.Roselani
See solution by Chris Nolet below: button.contentVerticalAlignment = UIControlContentVerticalAlignmentTop;Embrace
try both UIViewContentModeScaleAspectFill and UIViewContentModeScaleAspectFit. Fill will expand your image, while Fit to me is a fixed small size. UIViewContentModeScaleAspectFill solve my problem. I want a larger image for the buttonCouching
Content fill modes ALSO need to be set, and the button type needs to be custom. See this answer: https://mcmap.net/q/130503/-ios-gt-gt-uibutton-imageview-property-gt-gt-how-to-set-content-modePhenobarbitone
I love this answer. For years, I keep forgetting about it and coming back to it.Buller
Won't work in Swift because the hidden .imageView is not accessible.Paragraphia
Make sure your UIButton is of type .Custom!Impuissant
D
80

If you're dealing with the UIButton's image (as opposed to it's backgroundImage), setting the contentMode on the UIButton itself or on its imageView has no effect (despite what other answers say).

Alternatively do this instead:

self.button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentFill;
self.button.contentVerticalAlignment = UIControlContentVerticalAlignmentFill;

Or size your image accordingly.

OR just use a UIImageView (which properly respects contentMode) with a UITapGestureRecognizer attached to it, or a transparent UIButton on top of it.

Daugavpils answered 4/3, 2014 at 22:29 Comment(4)
This works for my case in iOS 7 whereas all the other ones here does not.Lavona
Setting contentHorizontalAlignment and contentVerticalAlignment is the correct solution. See https://mcmap.net/q/130503/-ios-gt-gt-uibutton-imageview-property-gt-gt-how-to-set-content-mode for an explanation.Embrace
This the answer I was looking for.Outlet
This answer is incorrect. You do need to set contentMode on the imageView if you want to do any kind of aspect fill or fit, as other answers have clarified. Refer: https://mcmap.net/q/128814/-uibutton-doesn-39-t-listen-to-content-mode-setting.Conspecific
C
63

Rather than setting the contentMode on the button itself, you'll want to set contentHorizontalAlignment and contentVerticalAlignment properties and (crucially) the contentMode for the button's imageView for any kind of aspect fill or fit:

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

You can also do other things like aligning the button's image to the top. If you don't need an aspect fill or fit, you just can set the alignment by itself:

button.contentVerticalAlignment = .top
Conspecific answered 30/10, 2011 at 10:9 Comment(1)
Thanks Chris - that solved my issue, or rather, it did in combination with contentEdgeInsets, in order to back my image off the top edge just a bit.Sisal
C
7

After a couple of hours of confusion, here's how I got it to work under iOS 3.2. As dusker mentioned, using setBackgroundImage instead of setImage did the job for me.

CGRect myButtonFrame = CGRectMake(0, 0, 250, 250);
UIImage *myButtonImage = [UIImage imageNamed:@"buttonImage"];

UIButton *myButton = [UIButton buttonWithType:UIButtonTypeCustom];

[myButton setBackgroundImage:myButtonImage forState:UIControlStateNormal];
[myButton setFrame: myButtonFrame];
[myButton setContentMode: UIViewContentModeScaleAspectFit];
Chopin answered 2/10, 2010 at 14:20 Comment(0)
O
6

The answer is to use a UIImageView with all the lovely Content Mode settings you want, and then layer a custom button on top of it. Dumb that you can't do that all in one shot, but it appears that you can't.

Oat answered 4/6, 2010 at 14:52 Comment(0)
B
6

These two things (which are quite hard to find initially) will stretch your UIButton image to fit the button size:

enter image description here enter image description here

one should always try to set such in the Storyboard rather than code.

Bazluke answered 12/12, 2019 at 21:28 Comment(1)
You are my hero!Kone
O
4

Found a fix for this. Set the adjustsImageWhenHighlighted property of UIButton to NO.

  UIButton *b = [[UIButton alloc] initWithFrame:rect];
        [b setImage:image forState:UIControlStateNormal];
        [b.imageView setContentMode:UIViewContentModeScaleAspectFill];
        [b setAdjustsImageWhenHighlighted:NO];

Hope this helps. Feel free to comment below, I will follow up on any questions that you have.

Ostrogoth answered 10/6, 2012 at 4:44 Comment(1)
Doesn't work. This merely prevents the button from highlighting.Embrace
O
4

My answer is similar to Kuba's. I needed my image to be programatically set.

UIImage *image = [[UIImage alloc] initWithContentsOfFile:...];
[button setBackgroundImage:image forState:UIControlStateNormal];
button.imageView.contentMode = UIViewContentModeScaleAspectFill; //this is needed for some reason, won't work without it.
for(UIView *view in button.subviews) {
    view.contentMode = UIViewContentModeScaleAspectFill;
}
Outlet answered 6/3, 2014 at 7:22 Comment(0)
T
3

Only solution which worked for me:

[button setImage:image forState:UIControlStateNormal];
button.imageView.contentMode = UIViewContentModeScaleAspectFill;
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentFill;
button.contentVerticalAlignment = UIControlContentVerticalAlignmentFill;
Thelmathem answered 14/7, 2015 at 12:10 Comment(0)
H
3

Swift 3

self.firstButton.imageView?.contentMode = .scaleAspectFill
Hamster answered 5/9, 2017 at 6:41 Comment(0)
S
2

For anyone experiencing this on iOS 15 and Xcode 13, see Matt's answer in this other question.

The behavior of Xcode changed and now defaults UIButtons from the library to the plain style, which prevents the child image from scaling as expected.

Shuttlecock answered 22/10, 2021 at 2:2 Comment(0)
Q
1

Instead of setImage try setBackgroundImage

Quartis answered 1/6, 2010 at 14:48 Comment(1)
Hmp. A, that's dumb, it should respect the CONTENT MODE setting for its CONTENT, right? And B, it STILL doesn't seem to respect the mode setting--it's now locked to "stretch" (which is, in this case, more like "squeeze"), and isn't scaling the content with respect to its aspect ratio.Oat
A
1

I believe we have a simple interface builder issue here - apparently the IB ignores any content-mode changes AFTER you have set the image-property.

the solution is as simple: set the content mode, remove previously set image-names (make sure you remove it in all states, default, highlighted etc.), then re-enter the desired image-names in all desired states - et voilà.

Acrylonitrile answered 19/1, 2013 at 20:18 Comment(1)
the same goes to programmatically set content-mode - do not set the image BEFORE setting the content-modeAcrylonitrile
R
0

I also advice to have a look at the adjustsImageWhenHighlighted UIButton property to avoid weird deformations of the image, when the button is pressed.

Raymund answered 7/10, 2010 at 16:54 Comment(0)
T
0

In trying to figure this out, my method got a bit hackier as time went on, and I wound up subclassing UIButton and overriding setHighlighted:

For me it works to just knock down the image alpha to .5, because they're on a black background.

However, it only works if I comment out [super setHighlighted:] (where it appears the image-stretchy code is going on), which just doesn't feel like the right way to solve this at all...everything seems to be working fine, though. We'll see how it holds up as I keep working on it.

- (void)setHighlighted:(BOOL)highlight {
    if (highlight) {
        [self.imageView setAlpha:.5];
    }  else {
        [self.imageView setAlpha:1];        
    }

//    [super setHighlighted:highlight];
}
Typist answered 28/2, 2012 at 21:19 Comment(1)
On the off-chance anyone finds this relevant, my above method seemed to work OK until I put the buttons on a UIScrollView, where sometimes the button's highlight got "stuck," which is what I suspected might eventually happen.Typist
T
0

If anyone looking for answer that work in iOS 6 and iOS 7 and storyboard:

You can set image in your storyboard:

enter image description here

And then:

for(UIView* testId in self.subviews) {
    if([testId isKindOfClass:[UIImageView class]])
        [testId setContentMode:UIViewContentModeScaleAspectFill];
}
Toledo answered 21/6, 2013 at 14:1 Comment(0)
J
0

If the UIButton does not seem to listen to the layout constraint settings, do check whether the images are larger than the button size. Always use the @2x and @3x images for retina resolutions.

Jibe answered 20/9, 2016 at 4:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.