How to not stretch an image in UIButton - ios

I'm trying to create a custom UITableViewCell programmatically and one of the subviews of this cell is going to be a button with an image in it (a simple image of a magnifying glass). However, I want the button's image to be centered and scaled proportionately down to fit in the button and NOT to be stretched to fill the entire button. Below is my code where self refers to the custom UITableViewCell which I am placing the button into.
self.myButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[self.myButton setBackgroundImage:[UIImage imageNamed: #image_name_here"] forState:UIControlStateNormal];
self.myButton.frame = CGRectMake(...//something, something)
self.myButton.imageView.contentMode = UIViewContentModeCenter;
[self.contentView addSubview:self.mySearchHelpButton];
Right now the image stretches to fill the entire button rather than scaling proportionately so that it fits nicely.
I have also tried setting the contentMode to UIViewContentModeScaleAspectFill but this doesn't seem to change anything. In fact, none of the different contentModes seem to change anything.

Have you tried setImage instead of setBackgroundImage?

Make sure the button is a Custom UIButton
Just using setImage: did not work for me on iOS9.
But it worked combined with myButton.imageView.contentMode = UIViewContentMode.ScaleAspectFit;

In Swift...
button.imageView?.contentMode = . scaleAspectFit

Storyboard
Property image.
You must define specific property in Runtime Attributes of Identity inspector – imageView.contentMode, where you set value relatively to rawValue position of enum UIViewContentMode. 1 means scaleAspectFit.
And button's alignment, in Attributes inspector:

Swift 4.2
myButton.imageView!.contentMode = .scaleAspectFit
Swift earlier version
myButton.imageView!.contentMode = UIViewContentMode.scaleAspectFit

Swift 4.X and 5.X
button.imageView!.contentMode = .scaleAspectFit
button.contentVerticalAlignment = .fill
button.contentHorizontalAlignment = .fill

I think you guys are missing the obvious issue. The image is too large for the button and iOS then has to guess how you want to scale it. Make sure your images are the right size and you won't have this issue. Of course this is for static images and buttons that you know the size for up-front -- not dynamic content.

Related

Programmatically set UIBarButtonItem fill color?

I have a set of UIBarButtonItems from png files inside a UIToolbar.
When a user clicks on an icon, I want that icon to be filled with a color to indicate a state change.
Is it possible to do this with a single set of images (maybe by programmatically changing some attributes) or do I necessarily need two sets of images (on for each state) ?
make sure your UIImage's renderingMode is UIImageRenderingModeAlwaysTemplate first
if you initWithImage then change UIBarButtonItem's tintColor.
Although UIBarButtonItem is not a view, its tintColor property behaves
the same as that of UIView.
else if you initWithCustomView in which with a UIImageView then change the imageView's tintColor might work too
else you can setItems: animated:NO to UIToolBar every time after clicks which might not be a elegant one
It is very straight forward. You just need to do the following in code :
imgView.image = [imgView.image
imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
imgView.tintColor = [UIColor someColor];

AspectFill does not fill the UIImageView

I have a button that has contentMode set to UIViewContentModeScaleAspectFill. I set it like this:
self.itemImage.imageView.contentMode = UIViewContentModeScaleAspectFill;
I also set it to Mode - Aspect Fill in IB for the button, just in case. And when I set the image, I DON'T set is as a background image:
[self.itemImage setImage:image forState:UIControlStateNormal];
However, occasionally my image does not fill the button (which I want it to do), like in this case:
See grey spaces on each side. Occurs on both iOS 6 and 7.
Any ideas?
At long last, I managed to find the solution that works in Alfie's answer to this SO question.
In short, if your UIButton (or its hidden imageView) does not respond to contentMode, use this code:
self.button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentFill;
self.button.contentVerticalAlignment = UIControlContentVerticalAlignmentFill;

How to create and add UIButton with multiline label from code using autolayout?

There's are a lot of links how to size a label, however there's a lack of info how to resize UIButton according to its label size. Currently, I have screen view set with autolayout (all the system constraints are added from IB) and everything works fine, but need to add dynamic content at the bottom of the screen. I need to create and add random number of buttons with random length titles. So, here's code fragment for creating and adding the buttons:
// Loop
UIButton *myButton = [UIButton new];
UIButton.frame = CGRectMake(0, previousButtonOriginY, self.view.bounds.size.width, 20);
myButton.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
myButton.titleLabel.textAlignment = NSTextAlignmentLeft;
myButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
[myButton setTitle:#"very long title..." forState:UIControlStateNormal];
myButton.titleLabel.numberOfLines = 0;
[self.footerView addSubview:myButton];
The problem is the frame height of the button stays 20px but the label string is shown on three lines and so overlaps with the other button title. If I add [myButton sizeToFit] then button width is resized to fit all the text into one line and so the title goes beyond the screen.
If I add [myButton sizeThatFits:CGSizeMake(320, 100)] then resize is not working at all. I know sizeToFit should not be called at all because it's not the part of autolayout, however need suggestions on that how easily to make button fit its label to screen 320 width.
I feel I need to add system constraints from code, but haven't done that before, so not sure how it should look like. I probably need a ton of constraints to be added from code in order to get this simple thing working :)
Instead of using Autolayout, you could just use a collection view which better options for you to lay out elements such as buttons.
It is better able to handle layouts under rotation as well.
Or you can use this code, it worked for me...
For example, substitute your desired padding values here:
UIButton* myButton = [[UIButton alloc] init];
// setup some autolayout constraints here
myButton.titleEdgeInsets = UIEdgeInsetsMake(-desiredBottomPadding,
-desiredRightPadding,
-desiredTopPadding,
-desiredLeftPadding);
Combined with the right autolayout constraints, you end up with an auto-resizing button which contains an image and text!
For this kind of customizations you'll need to create a subclass of UIButton. In the subclass you can add a customized label which benefits your needs. You can also override to setTitle:forState method to automatically update your customized label.

Add an image inside UIButton as an accesory

I have the following UIButton I want to add an Image inside to the far right. Like an accessoryView
I am already using backgroundImage and I want to avoid combining the 2 images into 1.
Is it possible to add another image inside the UIButton that is an AccesoryView lets say an image that has a PLUS to the far right? ( I am looking to a way to insert the new image inside the Button)
#luisCien comment
I tried
[_addImage setImage:[UIImage imageNamed:#"shareMe"] forState:UIControlStateNormal];
_addImage.contentHorizontalAlignment =
UIControlContentHorizontalAlignmentRight;
and it looks like this, I would like to image to be on the right side of the text.
Just add it as a subview and choose your coordinates..Try something like this:
UIImageView *yourPlusSign = [[UIImageView alloc]initWithImage:[UIImage imageNamed:#"yourPlusSignImageTitle"]];
yourPlusSign.frame = CGRectMake(x, y, width, height);//choose values that fit properly inside the frame of your baseButton
//or grab the width and height of yourBaseButton and change accordingly
yourPlusSign.contentMode=UIViewContentModeScaleAspectFill;//or whichever mode works best for you
[yourBaseButton addSubview:yourPlusSign];
You can use the contentHorizontalAlignment property of UIButton and set it to UIControlContentHorizontalAlignmentRight this will position the image you add to it to the far right.
Edit:
As suggested by #danypata you can also use UIButton's property imageEdgeInsets to leave
some margin around your 'accessoryView' like so:
[_addImage setImageEdgeInsets:UIEdgeInsetsMake(5, 0, 5, 10)];
Regarding having the text on the left and the image on the right, I believe it's not possible (someone please correct me). For that I would either create my own custom button by extending UIControl or add a UILabel and set it's frame (as opposed to using UIButton's setTitle:forState:) and add it as a subview of the button.
Hope this helps!

Why does a custom UIButton image does not resize in Interface Builder?

Why does a custom UIButton image not resize with the button?
I set its view mode to Scale to Fill in Interface Builder, but unlike a UIImageView image, it doesn't respect that setting.
Am I looking in the wrong place or is it not possible?
While this may not be quite what you're after - the background image does scale.
Try this:
[button setImage:image forState:UIControlStateNormal];
button.contentVerticalAlignment = UIControlContentVerticalAlignmentFill;
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentFill;
In Swift:
button.contentVerticalAlignment = .fill
button.contentHorizontalAlignment = .fill
Remember to set Control Alignment settings for the button
Inside Attribute Inspector first set Type "Custom" and Style "Default" then Set alignment Horizontal and Vertical both "fill".
I know this is old, but I think that the correct answer is that you should set the content mode to the "hidden" UIImageView that is inside the UIButton:
button.imageView.contentMode = UIViewContentModeScaleAspectFill;
This will change the content mode for the images views of the images set with:
[button setImage:yourAwesomeImage forState:UIControlStateNormal];
//Or any other state
Just do (From Design OR From Code):
From Design
Open your xib OR Storyboard.
Select button
Inside Attribute Inspector (Right side) > In "Control" section > select last 4th option for both Horizontal and Verical.
[For Point#3: Change Horizontal and vertical Align to UIControlContentHorizontalAlignmentFill and UIControlContentVericalAlignmentFill]
From Code
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentFill;
button.contentVerticalAlignment = UIControlContentVerticalAlignmentFill;
Interface Builder
To get images in UIButtons to scale the same way as UIViews in interface Builder follow these steps:
Select your UIButton and from the Identity Inspector add the following to User Defined Runtime Attributes:
imageView.contentMode Number 1
Where number is the enum number of contentMode, eg:
0 = Scale to fill
1 = Aspect Fit
2 = Aspect Fill
etc..
Then open the Attributes editor for your UIButton and under Control set the Alignment to Fill (last button on right) for both horizontal and vertical.
SWIFT 3
Note you can also do this in code with the following:
button.imageView?.contentMode = .scaleAspectFit
button.contentVerticalAlignment = .fill
button.contentHorizontalAlignment = .fill
Updating this post will full answer for Swift 5:
//Set button's image
let image = UIImage(systemName: "heart.fill") //Your image here
button.setImage(image, for: .normal)
//Optional: Remove background image if you added one on accident
button.setBackgroundImage(nil, for: .normal)
//Optional: Set button's title (displayed next to button's image)
button.setTitle(nil, for: .normal)
//The button's "content" includes the title label and image
//So we set the button's content to fill the button
//If title label is nil, the content will only show the image
button.contentVerticalAlignment = .fill
button.contentHorizontalAlignment = .fill
//Set button's image to desired content mode (aspectFit avoids distortion)
//This restricts the image from resizing to fill the entire content area
button.imageView?.contentMode = .scaleAspectFit
Try setting the Clip subviews property in Interface Builder to true.
Hope that helps.
The best solution I found so far in Swift 2.1 is the following:
self.imageButton.imageView?.clipsToBounds = true
self.imageButton.imageView?.contentMode = UIViewContentMode.ScaleAspectFit
This will scale the image so it has an aspect fit in the imageView.
Just get a real UIimageview and place a UIbutton on top of it and via layout menu set it to back. Then you can scale your image properly.

Resources