ios UIButton的:如何中心的图像,并使用imageEdgeInsets和titleEdgeInsets文本?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2451223/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
UIButton: how to center an image and a text using imageEdgeInsets and titleEdgeInsets?
提问by reinaldoluckman
If I put only an image in a button and set the imageEdgeInsets more close to the top, the image stays centered and all works as expected:
如果我只在按钮中放置一个图像并将 imageEdgeInsets 设置得更靠近顶部,则图像会保持居中并且一切正常:
[button setImage:image forState:UIControlStateNormal];
[button setImageEdgeInsets:UIEdgeInsetsMake(-15.0, 0.0, 0.0, 0.0)];
If I put only a text in a button and set titleEdgeInsets more close to the bottom, the text stays centered and all works as expected:
如果我只在按钮中放置一个文本并将 titleEdgeInsets 设置得更靠近底部,则文本会保持居中,并且一切都按预期工作:
[button setTitle:title forState:UIControlStateNormal];
[button setTitleEdgeInsets:UIEdgeInsetsMake(0.0, 0.0, -30, 0.0)];
But, if I put the 4 lines together, the text interferes with the image and both lost the center alignment.
但是,如果我把4线齐,文字干扰了图像,并双双落败的中心对齐。
All my images has 30 pixels width, and if I put 30 in the left parameter of UIEdgeInsetMake for setTitleEdgeInsets, the text is centered again. The problem is that the image never gets centered because it appears that it is dependent of the button.titleLabel size. I already tried many calculations with button size, image size, titleLabel size and never get both perfectly centered.
我所有的图像都有 30 像素的宽度,如果我在 UIEdgeInsetMake 的左侧参数中为 setTitleEdgeInsets 放置 30,则文本再次居中。问题是图像永远不会居中,因为它似乎取决于 button.titleLabel 的大小。我已经尝试了许多关于按钮大小、图像大小、titleLabel 大小的计算,但从未完全居中。
Someone already had the same problem?
有人已经遇到同样的问题了吗?
回答by Jesse Crossen
For what it's worth, here's a general solution to positioning the image centered above the text without using any magic numbers. Note that the following code is outdated and you should probably use one of the updated versions below:
对于它的价值,下面就以上面的文字为中心的形象定位,而无需使用任何魔法数字的通用解决方案。请注意,以下代码已过时,您可能应该使用以下更新版本之一:
// 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);
The following version contains changes to support iOS 7+that have been recommended in comments below. I haven't tested this code myself, so I'm not sure how well it works or whether it would break if used under previous versions of iOS.
以下版本包含以下评论中推荐的支持iOS 7+ 的更改。我自己还没有测试过这段代码,所以我不确定它的效果如何,或者如果在以前版本的 iOS 下使用它是否会中断。
// 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.image.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.text sizeWithAttributes:@{NSFontAttributeName: button.titleLabel.font}];
button.imageEdgeInsets = UIEdgeInsetsMake(
- (titleSize.height + spacing), 0.0, 0.0, - titleSize.width);
// increase the content height to avoid clipping
CGFloat edgeOffset = fabsf(titleSize.height - imageSize.height) / 2.0;
button.contentEdgeInsets = UIEdgeInsetsMake(edgeOffset, 0.0, edgeOffset, 0.0);
Swift 5.0version
斯威夫特 5.0版本
extension UIButton {
func alignVertical(spacing: CGFloat = 6.0) {
guard let imageSize = imageView?.image?.size,
let text = titleLabel?.text,
let font = titleLabel?.font
else { return }
titleEdgeInsets = UIEdgeInsets(
top: 0.0,
left: -imageSize.width,
bottom: -(imageSize.height + spacing),
right: 0.0
)
let titleSize = text.size(withAttributes: [.font: font])
imageEdgeInsets = UIEdgeInsets(
top: -(titleSize.height + spacing),
left: 0.0,
bottom: 0.0, right: -titleSize.width
)
let edgeOffset = abs(titleSize.height - imageSize.height) / 2.0
contentEdgeInsets = UIEdgeInsets(
top: edgeOffset,
left: 0.0,
bottom: edgeOffset,
right: 0.0
)
}
}
回答by reinaldoluckman
Found how.
发现如何。
First, configure the text of titleLabel
(because of styles, i.e, bold, italic, etc). Then, use setTitleEdgeInsets
considering the width of your image:
首先,配置文本titleLabel
(因为样式,即粗体、斜体等)。然后,setTitleEdgeInsets
考虑图像的宽度使用:
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[button setTitle:title forState:UIControlStateNormal];
[button.titleLabel setFont:[UIFont boldSystemFontOfSize:10.0]];
// Left inset is the negative of image width.
[button setTitleEdgeInsets:UIEdgeInsetsMake(0.0, -image.size.width, -25.0, 0.0)];
After that, use setTitleEdgeInsets
considering the width of text bounds:
之后,使用setTitleEdgeInsets
考虑文本边界的宽度:
[button setImage:image forState:UIControlStateNormal];
// Right inset is the negative of text bounds width.
[button setImageEdgeInsets:UIEdgeInsetsMake(-15.0, 0.0, 0.0, -button.titleLabel.bounds.size.width)];
Now the image and the text will be centered (in this example, the image appears above the text).
现在图像和文本将居中(在本例中,图像出现在文本上方)。
Cheers.
干杯。
回答by algal
You can do it with this Swift extension, which was based in part on Jesse Crossen's answer:
您可以使用这个 Swift 扩展来实现,它部分基于 Jesse Crossen 的回答:
extension UIButton {
func centerLabelVerticallyWithPadding(spacing:CGFloat) {
// update positioning of image and title
let imageSize = self.imageView.frame.size
self.titleEdgeInsets = UIEdgeInsets(top:0,
left:-imageSize.width,
bottom:-(imageSize.height + spacing),
right:0)
let titleSize = self.titleLabel.frame.size
self.imageEdgeInsets = UIEdgeInsets(top:-(titleSize.height + spacing),
left:0,
bottom: 0,
right:-titleSize.width)
// reset contentInset, so intrinsicContentSize() is still accurate
let trueContentSize = CGRectUnion(self.titleLabel.frame, self.imageView.frame).size
let oldContentSize = self.intrinsicContentSize()
let heightDelta = trueContentSize.height - oldContentSize.height
let widthDelta = trueContentSize.width - oldContentSize.width
self.contentEdgeInsets = UIEdgeInsets(top:heightDelta/2.0,
left:widthDelta/2.0,
bottom:heightDelta/2.0,
right:widthDelta/2.0)
}
}
This defines a function centerLabelVerticallyWithPadding
that sets the title and image insets appropriately.
这定义了一个centerLabelVerticallyWithPadding
适当设置标题和图像插入的函数。
It also sets the contentEdgeInsets, which I believe is necessary to ensure that intrinsicContentSize
still works correctly, which would need to use Auto Layout.
它还设置了 contentEdgeInsets,我认为这对于确保intrinsicContentSize
仍然正常工作是必要的,这需要使用自动布局。
I believe all solutions which subclass UIButton are technically illegitimate, since you are not supposed to subclass UIKit controls. I.e., in theory they might break in future releases.
我相信所有继承 UIButton 的解决方案在技术上都是非法的,因为你不应该继承 UIKit 控件。也就是说,理论上它们可能会在未来的版本中中断。
回答by Thomas Verbeek
Edit: Updated for Swift 3
编辑:针对 Swift 3 更新
In case you're looking for a Swift solution of Jesse Crossen's answer, you can add this to a subclass of UIButton:
如果您正在寻找 Jesse Crossen 答案的 Swift 解决方案,您可以将其添加到 UIButton 的子类中:
override func layoutSubviews() {
let spacing: CGFloat = 6.0
// lower the text and push it left so it appears centered
// below the image
var titleEdgeInsets = UIEdgeInsets.zero
if let image = self.imageView?.image {
titleEdgeInsets.left = -image.size.width
titleEdgeInsets.bottom = -(image.size.height + spacing)
}
self.titleEdgeInsets = titleEdgeInsets
// raise the image and push it right so it appears centered
// above the text
var imageEdgeInsets = UIEdgeInsets.zero
if let text = self.titleLabel?.text, let font = self.titleLabel?.font {
let attributes = [NSFontAttributeName: font]
let titleSize = text.size(attributes: attributes)
imageEdgeInsets.top = -(titleSize.height + spacing)
imageEdgeInsets.right = -titleSize.width
}
self.imageEdgeInsets = imageEdgeInsets
super.layoutSubviews()
}
回答by Tod Cunningham
There are some great examples in here, but I couldn't get this to work in all cases when also dealing with multiple lines of text (text wrapping). To finally get it to work I combined a couple of the techniques:
这里有一些很好的例子,但是在处理多行文本(文本换行)时,我无法在所有情况下都使用它。为了最终让它发挥作用,我结合了几种技术:
I used Jesse Crossen example above. However, I fixed a text height issue and I added the ability to specify a horizontal text margin. The margin is useful when allowing text to wrap so it doesn't hit the edge of the button:
// the space between the image and text CGFloat spacing = 10.0; float textMargin = 6; // get the size of the elements here for readability CGSize imageSize = picImage.size; CGSize titleSize = button.titleLabel.frame.size; CGFloat totalHeight = (imageSize.height + titleSize.height + spacing); // get the height they will take up as a unit // lower the text and push it left to center it button.titleEdgeInsets = UIEdgeInsetsMake( 0.0, -imageSize.width +textMargin, - (totalHeight - titleSize.height), +textMargin ); // top, left, bottom, right // the text width might have changed (in case it was shortened before due to // lack of space and isn't anymore now), so we get the frame size again titleSize = button.titleLabel.bounds.size; button.imageEdgeInsets = UIEdgeInsetsMake(-(titleSize.height + spacing), 0.0, 0.0, -titleSize.width ); // top, left, bottom, right
Make sure you setup the text label to wrap
button.titleLabel.numberOfLines = 2; button.titleLabel.lineBreakMode = UILineBreakModeWordWrap; button.titleLabel.textAlignment = UITextAlignmentCenter;
This will mostly work now. However, I had some buttons that wouldn't render their image correctly. The image was either shifted to far to the right or left (it wasn't centered). So I used an UIButton layout override technique to force the imageView to be centered.
@interface CategoryButton : UIButton @end @implementation CategoryButton - (void)layoutSubviews { // Allow default layout, then center imageView [super layoutSubviews]; UIImageView *imageView = [self imageView]; CGRect imageFrame = imageView.frame; imageFrame.origin.x = (int)((self.frame.size.width - imageFrame.size.width)/ 2); imageView.frame = imageFrame; } @end
我使用了上面的 Jesse Crossen 示例。但是,我修复了文本高度问题,并添加了指定水平文本边距的功能。当允许文本换行时,边距很有用,这样它就不会碰到按钮的边缘:
// the space between the image and text CGFloat spacing = 10.0; float textMargin = 6; // get the size of the elements here for readability CGSize imageSize = picImage.size; CGSize titleSize = button.titleLabel.frame.size; CGFloat totalHeight = (imageSize.height + titleSize.height + spacing); // get the height they will take up as a unit // lower the text and push it left to center it button.titleEdgeInsets = UIEdgeInsetsMake( 0.0, -imageSize.width +textMargin, - (totalHeight - titleSize.height), +textMargin ); // top, left, bottom, right // the text width might have changed (in case it was shortened before due to // lack of space and isn't anymore now), so we get the frame size again titleSize = button.titleLabel.bounds.size; button.imageEdgeInsets = UIEdgeInsetsMake(-(titleSize.height + spacing), 0.0, 0.0, -titleSize.width ); // top, left, bottom, right
确保将文本标签设置为换行
button.titleLabel.numberOfLines = 2; button.titleLabel.lineBreakMode = UILineBreakModeWordWrap; button.titleLabel.textAlignment = UITextAlignmentCenter;
现在这将主要起作用。但是,我有一些按钮无法正确呈现其图像。图像要么向右或向左移动(它没有居中)。所以我使用了 UIButton 布局覆盖技术来强制使 imageView 居中。
@interface CategoryButton : UIButton @end @implementation CategoryButton - (void)layoutSubviews { // Allow default layout, then center imageView [super layoutSubviews]; UIImageView *imageView = [self imageView]; CGRect imageFrame = imageView.frame; imageFrame.origin.x = (int)((self.frame.size.width - imageFrame.size.width)/ 2); imageView.frame = imageFrame; } @end
回答by Bekir Onat Akin
I made a method for @TodCunningham's answer
我为@TodCunningham 的回答制定了一个方法
-(void) AlignTextAndImageOfButton:(UIButton *)button
{
CGFloat spacing = 2; // the amount of spacing to appear between image and title
button.imageView.backgroundColor=[UIColor clearColor];
button.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
button.titleLabel.textAlignment = UITextAlignmentCenter;
// get the size of the elements here for readability
CGSize imageSize = button.imageView.frame.size;
CGSize titleSize = button.titleLabel.frame.size;
// lower the text and push it left to center it
button.titleEdgeInsets = UIEdgeInsetsMake(0.0, - imageSize.width, - (imageSize.height + spacing), 0.0);
// the text width might have changed (in case it was shortened before due to
// lack of space and isn't anymore now), so we get the frame size again
titleSize = button.titleLabel.frame.size;
// raise the image and push it right to center it
button.imageEdgeInsets = UIEdgeInsetsMake(- (titleSize.height + spacing), 0.0, 0.0, - titleSize.width);
}
回答by Tommie C.
Nothing wrong with the other answers, however I just wanted to note that the same behavior can be accomplished visually within Xcode using zero lines of code. This solution is useful if you do not need a calculated value or are building with a storyboard/xib (otherwise other solutions apply).
没有错,其他的答案,但我只是想注意相同的行为可以直观地在Xcode使用零行代码来实现。如果您不需要计算值或使用故事板/xib 构建(否则其他解决方案适用),此解决方案很有用。
Note - I do understand that the OP's question is one requiring code. I am just providing this answer for completeness and as a logical alternative for those using storyboards/xibs.
注意 - 我确实理解 OP 的问题是一个需要代码的问题。我只是为了完整性而提供这个答案,并作为使用故事板/xib 的人的合乎逻辑的替代方案。
To modify spacing on image, title, and content views of a button using edge insets you can select the button/control and open the attributes inspector. Scroll down towards the middle of the inspector and find the section for Edge insets.
要使用边缘插入修改按钮的图像、标题和内容视图上的间距,您可以选择按钮/控件并打开属性检查器。向下滚动到检查器的中间,找到边缘插入部分。
One can also access and modify the specific edge insets for the title, image or content view .
还可以访问和修改标题、图像或内容视图的特定边缘插图。
回答by Doci
Updated answer of Jesse Crossenfor Swift 4:
Jesse Crossen为Swift 4更新的答案:
extension UIButton {
func alignVertical(spacing: CGFloat = 6.0) {
guard let imageSize = self.imageView?.image?.size,
let text = self.titleLabel?.text,
let font = self.titleLabel?.font
else { return }
self.titleEdgeInsets = UIEdgeInsets(top: 0.0, left: -imageSize.width, bottom: -(imageSize.height + spacing), right: 0.0)
let labelString = NSString(string: text)
let titleSize = labelString.size(withAttributes: [kCTFontAttributeName as NSAttributedStringKey: font])
self.imageEdgeInsets = UIEdgeInsets(top: -(titleSize.height + spacing), left: 0.0, bottom: 0.0, right: -titleSize.width)
let edgeOffset = abs(titleSize.height - imageSize.height) / 2.0;
self.contentEdgeInsets = UIEdgeInsets(top: edgeOffset, left: 0.0, bottom: edgeOffset, right: 0.0)
}
}
Use this way:
使用这种方式:
override func viewDidLayoutSubviews() {
button.alignVertical()
}
回答by Steven Kramer
Don't fight the system. If your layouts become too complex to manage using Interface Builder + perhaps some simple configuration code, do the layouts manually in a simpler way using layoutSubviews
- that's what it's for! Everything else will amount to hacks.
不要和系统作对。如果您的布局变得太复杂而无法使用 Interface Builder + 也许一些简单的配置代码进行管理,请以更简单的方式手动进行布局layoutSubviews
- 这就是它的用途!其他一切都将等同于黑客攻击。
Create a UIButton subclass and override its layoutSubviews
method to align your text & image programmatically. Or use something like https://github.com/nickpaulson/BlockKit/blob/master/Source/UIView-BKAdditions.hso you can implement layoutSubviews using a block.
创建一个 UIButton 子类并覆盖其layoutSubviews
方法以编程方式对齐文本和图像。或者使用类似https://github.com/nickpaulson/BlockKit/blob/master/Source/UIView-BKAdditions.h这样的东西,这样你就可以使用块来实现 layoutSubviews。
回答by Alex
Subclass UIButton
子类 UIButton
- (void)layoutSubviews {
[super layoutSubviews];
CGFloat spacing = 6.0;
CGSize imageSize = self.imageView.image.size;
CGSize titleSize = [self.titleLabel sizeThatFits:CGSizeMake(self.frame.size.width, self.frame.size.height - (imageSize.height + spacing))];
self.imageView.frame = CGRectMake((self.frame.size.width - imageSize.width)/2, (self.frame.size.height - (imageSize.height+spacing+titleSize.height))/2, imageSize.width, imageSize.height);
self.titleLabel.frame = CGRectMake((self.frame.size.width - titleSize.width)/2, CGRectGetMaxY(self.imageView.frame)+spacing, titleSize.width, titleSize.height);
}