Separate image's position from title's length on UIButton - ios

UIButton image position is dependent of the current title length for the same image.
The first UIButton image is centered if there is no title set.
And the second one is shifted to the left when there is one ("like").
How can I always keep my UIButton image at the center of my button? Even if the title appears on the top of it?
I already tried with imageInset and it's the same problem. I'd also like not to use a different UIImageView other than the default UIButton's one.

You can use below code...
// the space between the image and text
CGFloat spacing = 6.0;
// lower the text and push it left so it appears centered
// below the image
CGSize imageSize = button.imageView.frame.size;
button.titleEdgeInsets = UIEdgeInsetsMake(
0.0, - imageSize.width, - (imageSize.height + spacing), 0.0);
// raise the image and push it right so it appears centered
// above the text
CGSize titleSize = button.titleLabel.frame.size;
button.imageEdgeInsets = UIEdgeInsetsMake(
- (titleSize.height + spacing), 0.0, 0.0, - titleSize.width);
Source: UIButton: how to center an image and a text using imageEdgeInsets and titleEdgeInsets?
You can try for alternate solutions using below link
UIButton Image + Text IOS
Hope it helps you...

Related

Align both image and UILabel of the UIButton in the left

I currently have :
using this code:
button.semanticContentAttribute = UISemanticContentAttributeForceRightToLeft;
button.transform = CGAffineTransformMakeScale(-1.0, 1.0);
button.titleLabel.transform = CGAffineTransformMakeScale(-1.0, 1.0);
button.imageView.transform = CGAffineTransformMakeScale(-1.0, 1.0);
What I'm trying to achieve is this:
Where the image and UILabel should be on the left side of the UIButton, not in the middle.
Try contentHorizontalAlignment property.
Objective-C
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
Swift
button.contentHorizontalAlignment = .left
To done this in Interface Builder you just need to change "alignment" property....
1.In attribute inspector of button change alignment Property to first option
2. this will be your output
3.if you want some space between image and title you can give from size inspector (just give some space in left property of title insets)
4.Final Output

Center image by first line of UILabel text

I want to center image to center Y position of first line of text of my UILabel. I use masonry to set Auto Layout constraints like that:
[_haveReadIndicatorImgView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.left.equalTo(self.contentView).offset(SMALL_OFFSET);
make.height.width.equalTo(#(8));
}];
[_topTxtlbl mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(_haveReadIndicatorImgView.mas_right).offset(TINY_OFFSET);
make.top.equalTo(_haveReadIndicatorImgView.mas_top);
make.right.equalTo(self.arrowImgView.mas_left).offset(-SMALL_OFFSET);
make.bottom.equalTo(_dateTxtLbl.mas_top).offset(-SMALL_OFFSET);
}];
It should be pretty strightforward. I simply attach top of UIImageView to top of my Label.
But take a look at screen.
Top edges of UIImageView (gray dot) and label are equal, but how to make UIImageView to be centered to first line of text like that?
Thanks.
Actually there is a way of doing this! If you use plain old AutoLayout this can be done with the following snippet:
// Aligns the icon to the center of a capital letter in the first line
let offset = label.font.capHeight / 2.0
// Aligns the icon to the center of the whole line, which is different
// than above. Especially with big fonts this makes a visible difference.
let offset = (label.font.ascender + label.font.descender) / 2.0
let constraints: [NSLayoutConstraint] = [
imageView.centerYAnchor.constraint(equalTo: label.firstBaselineAnchor, constant: -offset),
imageView.trailingAnchor.constraint(equalTo: label.leadingAnchor, constant: -10)
]
NSLayoutConstraint.activate(constraints)
The first constraint will display your icon at the Y center of the first line of your label. The second one puts your icon left of the label and creates a 10pt space between them.
Hope this helps!
You derive the middle of the first line by using the lineHeight for the font of your label.
let lineHeight = ceil(multiLineLabel.lineHeight)
let center = lineHeight / 2
Now that you have the center, you can center the haveReadIndicatorImgView's centerYAnchor to the top of your label with a constant: center
I solved this recently by adding a hidden single line label in exactly the same location and font as the multiline one, without a bottom constraint.
Then you can simply align the icon image .centerY to the hidden label's .centerY.
I achieve following by 2 steps:
1) Calculate expected height of label line of text with specific font:
+(CGSize)getSimpleSizeBasedOnFont:(CGFloat)font{
UILabel *lbl = [UILabel new];
lbl.text = #"Simple text";
lbl.font = [UIFont systemFontOfSize:font];
return [lbl.text sizeWithFont:lbl.font
constrainedToSize:lbl.frame.size
lineBreakMode:NSLineBreakByWordWrapping];
}
Then i add constraints to center Y of UIImage View with offset equal to 50% of that height:
CGFloat lblOffs = [Helper getSimpleSizeBasedOnFont:14].height;
[_haveReadIndicatorImgView mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerY.equalTo(_topTxtlbl.mas_top).offset(lblOffs/2);
make.left.equalTo(self.contentView).offset(SMALL_OFFSET);
make.height.width.equalTo(#(8));
}];
I have done this differently.
At first i align my imageView with label by FirstBaseLine.
And then i took an outlet of that LayoutConstraint
I have calculated an offset like below:
let offset = (label.font.capHeight + imageView.frame.size.height) / 2 //your bulleted image
I have discarded that offset from FirstBaseLine constant
firstBaseLineConstraintWithLabel.constant -= offset
Here is the output

How can I make Custom UIButton with image and text?

As I did on Android under a layout arrange an ImageView and TextView and set ClickListener on Layout so same thing how can I do on swift (3.0) Xcode 8.
There is no markup like XML. It's difficult to do with drag and drop.
How can I make this view with responsive for all device screens.
My UI image is below:
Thanks in advance.
after using CollectionView it shows like this
You can use this code to set image and title to button
let imageSize: CGSize = button.imageView!.image!.size
button.titleEdgeInsets = UIEdgeInsetsMake(26 , -imageSize.width, 0.0, 0.0);
let labelString = NSString(string: button.titleLabel!.text!)
let titleSize = labelString.sizeWithAttributes([NSFontAttributeName: button.titleLabel!.font])
button.imageEdgeInsets = UIEdgeInsetsMake(-15, 0.0, 0.0, -titleSize.width);
You can set UIImage + UIText inside UIButton , for reference
https://developer.apple.com/documentation/uikit/uibutton

Swift 3 | Button Label under Image AND resize Image

I'm displaying an image on top of a button, everything is OK on iPhone 7 e.g. :
I set button background color to blue to see the button frame.
Since I set an aspect ratio on my button's parent view, the button size change on small device, and on iPhone 4S, I have :
The button's images are not resized.
This is my code :
public extension UIButton {
func setButtonWithTextBehindImage () {
let spacing = CGFloat(0.0)
let imageSize = self.imageView?.frame.size
self.titleEdgeInsets = UIEdgeInsetsMake(0.0, -(imageSize?.width)!, -((imageSize?.height)! + spacing), 0.0)
let titleSize = self.titleLabel?.frame.size
self.imageEdgeInsets = UIEdgeInsetsMake(-((titleSize?.height)!), 0.0, 0.0, -(titleSize?.width)!)
}
}
I tried to set contentMode .scaleAspectFit on button and on button imageView but everytime my imageSize is (30, 30) (the size of my #1x image) and it doesn't resize to button size.
How can I do ? TY
Could you provide a bit more context on how you're using the code snippet you provided (maybe some code that we could try running)?
Documentation from https://developer.apple.com/reference/uikit/uibutton/1624010-titleedgeinsets mention insets being applied after the rectangle has been sized to fit. The time at which you're calling your function in the extension might have an effect on the expected resize not happening - as in, are you calling this when the view is loaded, after it appears, after subviews have been laid out.

How do I prevent a button's background image from stretching?

I'm allocating a UIButtonTypeCustom to a UIView with a background image that is smaller than the button's frame.
Reason why the image is smaller is because I'm trying to add more of a "target area" for the UIButton. However, the image is being scaled to the full size of the frame, rather than just being the image's size.
I have tried setting the UIButton and UIButton's imageView's contentMode property to UIViewContentModeScaleAspectFit, but no luck, the image still gets stretched out.
Is there a way to do what I'm trying to do programmatically?
Thanks in advance!
A lot of people make the same mistake you do in regards to button images and then jump through hoops trying to make the button behave as they expect it to. Let's clear this up once and for all:
A UIButton has two types of images it can display -- a foreground image and a background image. The background image for a button is expected to replace the button's background texture. As such, it makes sense that it stretches to fill the entire background. However, the button's foreground image is expected to be an icon that may or may not display alongside text; it will not stretch. It may shrink if the frame is smaller than the image, but it will not stretch. You can even set the alignment of the foreground image using the Control alignment properties in Interface Builder.
A button's foreground and background image can be set in code like this:
// stretchy
[self setBackgroundImage:backgroundImage forState:UIControlStateNormal];
// not stretchy
[self setImage:forgroundImage forState:UIControlStateNormal];
You don't have access to the background imageView, but there is fully working workaround:
EDIT: There is an even better workaround then what I posted originally.
You can create a UIImage from any color, and call -setBackgroundImage:forState.
See bradley's answer, here:
https://stackoverflow.com/a/20303841/1147286
Original answer:
Instead of calling -setBackgroundImage:forState:, create a new UIImageView and add it as a subview of the button.
UIImageView *bgImageView = [[UIImageView alloc] initWithImage:img];
bgImageView.contentMode = UIViewContentModeScaleAspectFit;
[bgImageView setFrame:CGRectMake(0, 0, videoButton.frame.size.width, videoButton.frame.size.height)];
bgImageView.tag = 99;
[yourButton addSubview:bgImageView];
[yourButton bringSubviewToFront:yourButton.imageView];
Create the imageview
Set the content mode and frame
I also set a recognizable tag, so that when the screen rotates I can easily find my custom imageView in the button's subviews and reset its frame
Add it as a subview to the button
Bring the frontal imageView of the button to the front so our custom imageView doesn't overlap it
When the button needs to rotate just find the imageView by its tag and reset its frame:
UIImageView *bgImageView = (UIImageView *)[button viewWithTag:99];
[bgImageView setFrame:CGRectMake(0, 0, newWidth, newHeight)];
The cleanest and easiest way it probably to use the title insets of the button.
You set your image as the button image, and then you change the left title inset to match minus the width of your image:
myButton.titleEdgeInsets = UIEdgeInsetsMake(0, -myImage.width, 0, 0)
This will move the text back where it was before the image was added to its left. You can also use this value to add some padding to you button.
Stumbled on this problem too.
Adding image programmatically, as memmons thoroughly explained, did not help:(
I had a button 100x40 and image 100x100, it would appear squeezed, not fitted, as one would infer from "Aspect Fit" option. Actually, non of those view options had an effect.
I just had to rescale it so it would fit on a button, then use setImage:
UIImage *img=[UIImage imageNamed:#"myimage.png"];
CGImageRef imgRef = [img CGImage];
CGFloat imgW = CGImageGetWidth(imgRef);
CGFloat imgH = CGImageGetHeight(imgRef);
CGFloat btnW = myBttn.frame.size.width;
CGFloat btnH = myBttn.frame.size.height;
//get lesser button dimension
CGFloat minBtn=btnW;
if (btnW>btnH) {
minBtn=btnH;
}
//calculate scale using greater image dimension
CGFloat scl=imgH/minBtn;
if (imgW>imgH) {
scl=imgW/minBtn;
}
//scale image
UIImage *scaledImage = [UIImage imageWithCGImage:[img CGImage] scale:(img.scale * scl) orientation:(img.imageOrientation)];
//clean up
UIGraphicsEndImageContext();
//set it on a button
[myBttn setImage:scaledImage forState:UIControlStateNormal];
It is simple as:
ImageBtn.imageView?.contentMode = .scaleAspectFit
ImageBtn.setImage(chosenImage, for: .normal)
Another consideration is the BaseLine constraint. If your buttons have this constraint set (depicted as a horizontal or vertical line through multiple controls on your layout), it will cause your images to stretch without stretching the underlying button control. If your button is otherwise properly constrained (leading/trailing and top/bottom spaces, and etc...) removing the BaseLine constraint should have no impact on the layout, while allowing the foreground image to scale properly to the underlying button shape.
Answerbot answers the question with what is proper and correct to do. Don't fight the OS and use things as intended is always good advice. However, sometimes you need to break the rules.
I was able to mask the enlarged background image (not prevent it) by overlaying it with a black CAlayer then overlaying again with a properly resized image CAlayer. This was all done by creating a subclass of UIButton and overwriting the setHighlighted method.
NEED CODE?
- (void)setHighlighted:(BOOL)highlighted
{
super.highlighted = highlighted;
//
//Whenever an image needs to be highlighted, create a dimmed new image that is correctly sized. Below it is a englarged stretched image.
//
if (highlighted != _previousHighlightedSate)
{
_previousHighlightedSate = highlighted;
if (highlighted)
{
//Create a black layer so image can dim
_blackLayer = [CALayer layer];
_blackLayer.bounds = self.bounds;
CGRect rect = _blackLayer.bounds;
rect.size.width = rect.size.width*2;
rect.size.height = rect.size.height*2;
_blackLayer.bounds = rect;
_blackLayer.backgroundColor = [[UIColor blackColor] CGColor];
//create image layer
_nonStretchImageLayer = [CALayer layer];
_nonStretchImageLayer.backgroundColor = [UIColor blackColor].CGColor;
_nonStretchImageLayer.bounds = CGRectMake(0 , 0, self.bounds.size.width, self.bounds.size.height);
_nonStretchImageLayer.frame = CGRectMake(0 , 0, self.bounds.size.width, self.bounds.size.height);
_nonStretchImageLayer.contentsGravity = kCAGravityResizeAspect;//default is to resize
_nonStretchImageLayer.contents = (id)self.imageView.image.CGImage;
_nonStretchImageLayer.opacity = 0.5;
//add layers to image view
[self.imageView.layer addSublayer:_blackLayer];
[self.imageView.layer addSublayer:_nonStretchImageLayer];
}
else
{
//remove from image view
[_blackLayer removeFromSuperlayer];
[_nonStretchImageLayer removeFromSuperlayer];
//nil them out.
_blackLayer = nil;
_nonStretchImageLayer = nil;
}
}
Inspiration for this work around came from here
I guess the easiest solution is to use UIImage's resizableImageWithCapInsets method. Use UIEdgeInsetsMake to configure the free spaces.
might help someone
button.subviews.first?.contentMode = .scaleAspectFit
Swift version of Zoltán Matók answer
Just a copy of my code using SnapKit to do auto layout and syntatic sugar Then library for initilizations, it should work similar for normal Apples way of programatic layout.
let backButton = UIButton(type: .custom).then { (button) in
let image = UIImage(named: "backButtonIcon")?.withRenderingMode(.alwaysOriginal)
let imageView = UIImageView(image: image)
button.addSubview(imageView)
imageView.snp.makeConstraints { (make) in
make.center.equalTo(button.snp.center)
}
button.bringSubviewToFront(imageView)
button.contentMode = .scaleAspectFit
button.backgroundColor = .clear
button.isUserInteractionEnabled = true
button.isEnabled = true
button.imageView?.contentMode = .scaleAspectFit
button.imageView?.snp.makeConstraints({ (make) in
make.height.width.equalTo(24)
})
button.snp.makeConstraints { (make) in
make.height.width.equalTo(24)
}
}
You can use uibutton.imageView.contentMode for no stretching:
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(posX, posY, widthButton, heightButton);
[button setTitle:#"" forState:UIControlStateNormal];
[button setImage:[UIImage imageNamed:#"imageNamed"] forState:UIControlStateNormal];
[button setImage:[UIImage imageNamed:#"imageNamedHighlighted"] forState:UIControlStateHighlighted];
button.imageView.contentMode = UIViewContentModeScaleAspectFit;
[button addTarget:self action:#selector(functionMenu:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview: button];
What you need to do is add your image as a UIImageView.
Than add a button with transperent background (UIColor ClearColor) on top of it with your desired width and height.

Resources