Ios UIButton:如何使用imageEdgeInsets和titleEdgeInsets使图像和文本居中?
如果我只在按钮中放置一个图像,并将imageEdgeInsets设置得更靠近顶部,则图像将保持居中,并且所有操作均按预期进行:Ios UIButton:如何使用imageEdgeInsets和titleEdgeInsets使图像和文本居中?,ios,objective-c,uibutton,Ios,Objective C,Uibutton,如果我只在按钮中放置一个图像,并将imageEdgeInsets设置得更靠近顶部,则图像将保持居中,并且所有操作均按预期进行: [button setImage:image forState:UIControlStateNormal]; [button setImageEdgeInsets:UIEdgeInsetsMake(-15.0, 0.0, 0.0, 0.0)]; [button setTitle:title forState:UIControlStateNormal]; [button
[button setImage:image forState:UIControlStateNormal];
[button setImageEdgeInsets:UIEdgeInsetsMake(-15.0, 0.0, 0.0, 0.0)];
[button setTitle:title forState:UIControlStateNormal];
[button setTitleEdgeInsets:UIEdgeInsetsMake(0.0, 0.0, -30, 0.0)];
如果我只在按钮中放置一个文本,并将titleEdgeInsets设置为更靠近底部,则文本将保持居中,并且所有操作均按预期进行:
[button setImage:image forState:UIControlStateNormal];
[button setImageEdgeInsets:UIEdgeInsetsMake(-15.0, 0.0, 0.0, 0.0)];
[button setTitle:title forState:UIControlStateNormal];
[button setTitleEdgeInsets:UIEdgeInsetsMake(0.0, 0.0, -30, 0.0)];
但是,如果我把这4行放在一起,文本会干扰图像,并且两者都失去了中心对齐
我所有的图像都有30像素的宽度,如果我在setTitleEdgeInsets的UIEdgeInsetMake的左参数中输入30,文本将再次居中。问题是图像永远不会居中,因为它似乎取决于button.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)];
然后,考虑到文本边界的宽度,使用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)];
现在,图像和文本将居中(在本例中,图像显示在文本上方)
干杯。不管它值多少钱,这里有一个通用的解决方案,可以在不使用任何幻数的情况下将图像定位在文本上方的中心位置请注意,以下代码已过时,您可能应该使用以下更新版本之一:
// 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);
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)
}
}
以下版本包含以下评论中建议的支持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.0版本
扩展按钮{
func alignVertical(间距:CGFloat=6.0){
guard let imageSize=imageView?.image?.size,
设text=标题标签?.text,
让font=标题标签?.font
else{return}
titleEdgeInsets=UIEdgeInsets(
排名:0.0,
左键:size.width,
底部:-(图像大小、高度+间距),
右:0.0
)
让titleSize=text.size(带属性:[.font:font])
imageEdgeInsets=UIEdgeInsets(
顶部:-(标题大小、高度+间距),
左:0.0,
底部:0.0,
右:-titleSize.width
)
让边缘偏移=绝对值(titleSize.height-imageSize.height)/2.0
contentEdgeInsets=UIEdgeInsets(
顶部:边缘偏移,
左:0.0,
底部:边缘偏移,
右:0.0
)
}
}
使用按钮。titleLabel.frame.size.width
只要标签足够短而不会被截断,就可以正常工作。但是,当标签文本被截断时,定位不起作用。服用
CGSize titleSize = [[[button titleLabel] text] sizeWithFont:[[button titleLabel] font]];
即使标签文本被截断,对我也有效。我查看了现有答案,但我也发现设置按钮框是重要的第一步 下面是我使用的一个函数:
const CGFloat kImageTopOffset = -15;
const CGFloat kTextBottomOffset = -25;
+ (void) centerButtonImageTopAndTextBottom: (UIButton*) button
frame: (CGRect) buttonFrame
text: (NSString*) textString
textColor: (UIColor*) textColor
font: (UIFont*) textFont
image: (UIImage*) image
forState: (UIControlState) buttonState
{
button.frame = buttonFrame;
[button setTitleColor: (UIColor*) textColor
forState: (UIControlState) buttonState];
[button setTitle: (NSString*) textString
forState: (UIControlState) buttonState ];
[button.titleLabel setFont: (UIFont*) textFont ];
[button setTitleEdgeInsets: UIEdgeInsetsMake( 0.0, -image.size.width, kTextBottomOffset, 0.0)];
[button setImage: (UIImage*) image
forState: (UIControlState) buttonState ];
[button setImageEdgeInsets: UIEdgeInsetsMake( kImageTopOffset, 0.0, 0.0,- button.titleLabel.bounds.size.width)];
}
这里有一些很好的例子,但在处理多行文本(文本换行)时,我不能让它在所有情况下都起作用。为了让它最终发挥作用,我结合了两种技术:
// 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
不要与体制对抗。如果您的布局变得过于复杂,无法使用Interface Builder+或者一些简单的配置代码进行管理,请使用
layoutSubviews
以更简单的方式手动执行布局-这就是它的用途!其他一切都将成为黑客
创建UIButton子类并重写其
layoutSubviews
方法,以编程方式对齐文本和图像。或者使用类似的方法,以便使用块实现布局子视图。我为@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);
}
或者,您可以只使用此类别:
@interface UIButton (VerticalLayout)
- (void)centerVerticallyWithPadding:(float)padding;
- (void)centerVertically;
@end
@implementation UIButton (VerticalLayout)
- (void)centerVerticallyWithPadding:(float)padding
{
CGSize imageSize = self.imageView.frame.size;
CGSize titleSize = self.titleLabel.frame.size;
CGFloat totalHeight = (imageSize.height + titleSize.height + padding);
self.imageEdgeInsets = UIEdgeInsetsMake(- (totalHeight - imageSize.height),
0.0f,
0.0f,
- titleSize.width);
self.titleEdgeInsets = UIEdgeInsetsMake(0.0f,
- imageSize.width,
- (totalHeight - titleSize.height),
0.0f);
}
- (void)centerVertically
{
const CGFloat kDefaultPadding = 6.0f;
[self centerVerticallyWithPadding:kDefaultPadding];
}
@end
假设您希望文本和图像水平居中,则文本上方的图像: 将interface builder中的文本居中,并添加顶部插图(为图像腾出空间)。(将左侧插图保留为0)。使用interface builder选择图像-它的实际位置将从代码中设置,因此不要担心IB中的情况看起来不正常。与上面的其他答案不同,这实际上适用于所有当前支持的ios版本(5、6和7) 在代码中,抓取图像后,只需放弃按钮的ImageView(通过将按钮的图像设置为null)(这也会自动将文本居中,必要时进行包装)。然后用相同的帧大小和图像实例化自己的IVIEVIEW,并将其定位在中间。
通过这种方式,您仍然可以从interface builder中选择映像(虽然它不会像在simulator中一样在IB中对齐,但是,其他解决方案在所有受支持的ios版本中都不兼容)您可以使用此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)
}
}
这定义了一个函数centerLabelVerticallyWithPadding
,用于适当设置标题和图像插入
它还设置contentEdgeInsets,我认为这是确保intrinsicContentSize
仍然正常工作所必需的,这需要使用自动布局
我相信所有属于UIButton子类的解决方案在技术上都是非法的,因为您不应该将UIKit控件子类化。我
-(id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
_imageView = [[UIImageView alloc] initWithCoder:aDecoder];
}
return self;
}
[self.btn addSubview:_imageView];
[self.btn.titleLabel setTextAlignment:NSTextAlignmentCenter];
[_imageView setImage:[UIImage imageNamed:@"blahImageBlah]];
[_imageView sizeToFit];
_imageView.center = CGPointMake(ceilf(self.btn.bounds.size.width / 2.0f),
ceilf((self.btn.bounds.size.height / 2.0f) - 15));
[self.btn setTitle:@"Some new text" forState:UIControlStateNormal];
- (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);
}
CGFloat spacing = 3;
self.titleEdgeInsets = UIEdgeInsetsMake(0.0, - image.size.width, - (image.size.height + spacing), 0.0);
CGSize titleSize = [name sizeWithAttributes:@{NSFontAttributeName:self.titleLabel.font}];
self.imageEdgeInsets = UIEdgeInsetsMake(- (titleSize.height + spacing), 0.0, 0.0, - titleSize.width);
CGSize titleSize = button.titleLabel.frame.size;
CGSize titleSize = [button.titleLabel.text sizeWithAttributes: @{NSFontAttributeName:button.titleLabel.font}];
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()
}
override func layoutSubviews() {
super.layoutSubviews()
if let image = imageView?.image {
let margin = 30 - image.size.width / 2
let titleRect = titleRectForContentRect(bounds)
let titleOffset = (bounds.width - titleRect.width - image.size.width - margin) / 2
contentHorizontalAlignment = UIControlContentHorizontalAlignment.Left
imageEdgeInsets = UIEdgeInsetsMake(0, margin, 0, 0)
titleEdgeInsets = UIEdgeInsetsMake(0, (bounds.width - titleRect.width - image.size.width - margin) / 2, 0, 0)
}
}
[self.leftButton setImageEdgeInsets:UIEdgeInsetsMake(0, 10.0, 20.0, 10.0)];
[self.leftButton setTitleEdgeInsets:UIEdgeInsetsMake(60, -75.0, 0.0, 0.0)];
// the space between the image and text
let spacing = CGFloat(36.0);
// lower the text and push it left so it appears centered
// below the image
let imageSize = tutorialButton.imageView!.frame.size;
tutorialButton.titleEdgeInsets = UIEdgeInsetsMake(
0, -CGFloat(imageSize.width), -CGFloat(imageSize.height + spacing), 0.0);
// raise the image and push it right so it appears centered
// above the text
let titleSize = tutorialButton.titleLabel!.frame.size;
tutorialButton.imageEdgeInsets = UIEdgeInsetsMake(
-CGFloat(titleSize.height + spacing), CGFloat((tutorialButton.frame.width - imageSize.width) / 2), 0.0, -CGFloat(titleSize.width));
extension UIButton {
func alignTextUnderImage() {
guard let imageView = imageView else {
return
}
self.contentVerticalAlignment = .Top
self.contentHorizontalAlignment = .Center
let imageLeftOffset = (CGRectGetWidth(self.bounds) - CGRectGetWidth(imageView.bounds)) / 2//put image in center
let titleTopOffset = CGRectGetHeight(imageView.bounds) + 5
self.imageEdgeInsets = UIEdgeInsetsMake(0, imageLeftOffset, 0, 0)
self.titleEdgeInsets = UIEdgeInsetsMake(titleTopOffset, -CGRectGetWidth(imageView.bounds), 0, 0)
}
}
extension UIButton
{
func centerImageAndTextVerticaAlignment(spacing: CGFloat)
{
var titlePoint : CGPoint = convertPoint(center, fromView:superview)
var imageViewPoint : CGPoint = convertPoint(center, fromView:superview)
titlePoint.y += ((titleLabel?.size.height)! + spacing)/2
imageViewPoint.y -= ((imageView?.size.height)! + spacing)/2
titleLabel?.center = titlePoint
imageView?.center = imageViewPoint
}
}
extension UIButton {
func alignImageAndTitleVertically(padding: CGFloat = 6.0) {
let imageSize: CGSize = imageView!.image!.size
titleEdgeInsets = UIEdgeInsetsMake(0.0, -imageSize.width, -(imageSize.height + padding), 0.0)
let labelString = NSString(string: titleLabel!.text!)
let titleSize = labelString.size(attributes: [NSFontAttributeName: titleLabel!.font])
self.imageEdgeInsets = UIEdgeInsetsMake(-(titleSize.height + padding), 0.0, 0.0, -titleSize.width)
let edgeOffset = abs(titleSize.height - imageSize.height) / 2.0;
self.contentEdgeInsets = UIEdgeInsetsMake(edgeOffset, 0.0, edgeOffset, 0.0)
}
}
UIEdgeInsets imageEdgeInsets = self.remoteCommandsButtonLights.imageEdgeInsets;
imageEdgeInsets.left = [button.titleLabel.text sizeWithAttributes:@{NSFontAttributeName:[button.titleLabel font]}].width;
imageEdgeInsets.bottom = 14.0;
button.imageEdgeInsets = imageEdgeInsets;
UIEdgeInsets titleEdgeInsets = self.remoteCommandsButtonLights.titleEdgeInsets;
titleEdgeInsets.left = -button.currentImage.size.width;
titleEdgeInsets.top = 20.0;
button.titleEdgeInsets = titleEdgeInsets;
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)
}
}
override func viewDidLayoutSubviews() {
button.alignVertical()
}
func moveImageLeftTextCenter(imagePadding: CGFloat = 30.0){
guard let imageViewWidth = self.imageView?.frame.width else{return}
guard let titleLabelWidth = self.titleLabel?.intrinsicContentSize.width else{return}
self.contentHorizontalAlignment = .left
imageEdgeInsets = UIEdgeInsets(top: 0.0, left: imagePadding - imageViewWidth / 2, bottom: 0.0, right: 0.0)
titleEdgeInsets = UIEdgeInsets(top: 0.0, left: (bounds.width - titleLabelWidth) / 2 - imageViewWidth, bottom: 0.0, right: 0.0)
}
func moveImageRIghtTextCenter(imagePadding: CGFloat = 30.0){
guard let imageViewWidth = self.imageView?.frame.width else{return}
guard let titleLabelWidth = self.titleLabel?.intrinsicContentSize.width else{return}
self.contentHorizontalAlignment = .right
imageEdgeInsets = UIEdgeInsets(top: 0.0, left:0.0 , bottom: 0.0, right: imagePadding - imageViewWidth / 2)
titleEdgeInsets = UIEdgeInsets(top: 0.0, left:0.0 , bottom: 0.0, right:(bounds.width - titleLabelWidth) / 2 - imageViewWidth)
}
extension UIButton {
public func centerImageAndTextVertically(spacing: CGFloat) {
layoutIfNeeded()
let contentFrame = contentRect(forBounds: bounds)
let imageFrame = imageRect(forContentRect: contentFrame)
let imageLeftInset = bounds.size.width * 0.5 - imageFrame.size.width * 0.5
let imageTopInset = -(imageFrame.size.height + spacing * 0.5)
let titleFrame = titleRect(forContentRect: contentFrame)
let titleLeftInset = ((bounds.size.width - titleFrame.size.width) * 0.5) - imageFrame.size.width
let titleTopInmset = titleFrame.size.height + spacing * 0.5
imageEdgeInsets = UIEdgeInsets(top: imageTopInset, left: imageLeftInset, bottom: 0, right: 0)
titleEdgeInsets = UIEdgeInsets(top: titleTopInmset, left: titleLeftInset, bottom: 0, right: 0)
}
}