UIButton:如何使用imageEdgeInsets和titleEdgeInsets居中图像和文本?

如果我只在button中放置一个图像,并将imageEdgeInsets设置得更接近顶部,则图像将保持居中,并且所有function都按预期工作:

[button setImage:image forState:UIControlStateNormal]; [button setImageEdgeInsets:UIEdgeInsetsMake(-15.0, 0.0, 0.0, 0.0)]; 

如果我只在button中放置一个文本,并将titleEdgeInsets设置得更接近底部,则文本保持居中,并且所有的function都按预期工作:

 [button setTitle:title forState:UIControlStateNormal]; [button setTitleEdgeInsets:UIEdgeInsetsMake(0.0, 0.0, -30, 0.0)]; 

但是,如果我把4条线放在一起,文字干扰图像,并且都失去了中心alignment。

我所有的图像都有30像素的宽度,如果我把UIEdgeInsetMake的left参数设置为setTitleEdgeInsets,则文本再次居中。 问题是,图像永远不会居中,因为看起来它是依赖于button.titleLabel大小。 我已经尝试了许多button大小,图像大小,titleLabel大小的计算,永远不会完全居中。

有人已经有同样的问题?

对于什么是值得的,这里是一个通用的解决scheme,将图像定位在文本上方而不使用任何幻数。 请注意,以下代码已过时,您应该使用下面的更新版本之一

 // 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); 

以下版本包含对以下评论中推荐的支持iOS 7+的更改。 我还没有自己testing这个代码,所以我不确定它是如何工作的,或者如果在以前版本的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版本

 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(attributes: [NSFontAttributeName: 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) } } 

发现如何。

首先,configurationtitleLabel的文本(因为样式,即粗体,斜体等)。 然后,使用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)]; 

现在图像和文字将居中(在这个例子中,图像出现在文字上方)。

干杯。

你可以用这个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的所有解决scheme在技术上是非法的,因为你不应该inheritanceUIKit控件。 也就是说,从理论上讲,他们可能会在未来的版本中打破

编辑:更新为Swift 3

如果你正在寻找Jesse Crossen答案的Swift解决scheme,你可以把它添加到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() } 

我为@ 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); } 

这里有一些很好的例子,但是当处理多行文本(文本换行)时,我无法在所有情况下都能工作。 为了最终实现它,我结合了一些技巧:

  1. 我使用了上面的Jesse Crossen例子。 但是,我修复了文字高度问题,并添加了指定水平文字边距的function。 允许文本换行时边距非常有用,因此不会碰到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 
  2. 确保你设置文本标签来包装

     button.titleLabel.numberOfLines = 2; button.titleLabel.lineBreakMode = UILineBreakModeWordWrap; button.titleLabel.textAlignment = UITextAlignmentCenter; 
  3. 这现在大部分工作。 但是,我有一些button,不会正确呈现其图像。 图像被转移到左侧或右侧(不居中)。 所以我使用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 

不要打这个系统。 如果您的布局变得太复杂,无法使用Interface Builder +pipe理一些简单的configuration代码,那么使用layoutSubviews以更简单的方式手动执行布局 – 就是这么做的! 其他一切都将相当于黑客。

创build一个UIButton子类,并重写它的layoutSubviews方法来alignment你的文本和图像编程。 或者使用像https://github.com/nickpaulson/BlockKit/blob/master/Source/UIView-BKAdditions.h这样的东西,所以你可以使用块实现layoutSubviews。;

其他答案没有错,但是我只是想指出,同样的行为可以在Xcode使用零代码行可视化完成。 如果您不需要计算值或正在构build故事板/ xib(否则应用其他解决scheme),则此解决scheme非常有用。

注 – 我明白,OP的问题是需要代码的问题。 我只是提供这个答案的完整性,并作为使用故事板/ xibs的逻辑select。

要使用边缘插入来修改button的图像,标题和内容视图上的间距,可以selectbutton/控件并打开属性检查器。 向下滚动到检查员的中间,find边缘插图部分。

边缘插图

还可以访问和修改标题,图像或内容视图的特定边缘插页。

菜单选项

子类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); } 

有了这块代码,你会得到这样的东西 标题和图像对齐

 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) } } 

Jesse Crossen的回答只是一个小小的改变,使得它对我来说非常合适:

代替:

 CGSize titleSize = button.titleLabel.frame.size; 

我用过这个:

 CGSize titleSize = [button.titleLabel.text sizeWithAttributes: @{NSFontAttributeName:button.titleLabel.font}]; 

Swift 3.0语法的UIButton扩展:

 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) } } 

原始答案: https : //stackoverflow.com/a/7199529/3659227

使用button.titleLabel.frame.size.width工作正常,只要标签足够短,不被截断。 当标签文本被截断时,定位不起作用。 以

 CGSize titleSize = [[[button titleLabel] text] sizeWithFont:[[button titleLabel] font]]; 

即使在标签文本被截断时也适用于我。

我看了现有的答案,但我也发现设置button框架是重要的第一步。

这是我使用的一个function,需要照顾这个:

 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)]; } 

或者你可以使用这个类别:

 @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 

我的用例使得insets无法pipe理:

  1. button上的背景图像保持一致
  2. dynamic文本和图像的变化,其中string长度和图像大小变化

这就是我最终做的,我对它很满意:

  • 在背景图片上创build故事板上的button(带模糊和颜色的圆圈)。

  • 在我的类中声明一个UIImageView:

     @implementation BlahViewController { UIImageView *_imageView; } 
  • 在init上创build图像视图实例:

     -(id)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) { _imageView = [[UIImageView alloc] initWithCoder:aDecoder]; } return self; } 
  • 在viewDidLoad中添加一个新的图层到我们的图像视图的button,并设置文本alignment:

     [self.btn addSubview:_imageView]; [self.btn.titleLabel setTextAlignment:NSTextAlignmentCenter]; 
  • 在button单击方法中,将我select的叠加图像添加到图像视图中,将其大小调整为适合图像,并将其居中放置在button中,但将其向上移动15,以便将文本偏移量放置在其下方:

     [_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]; 

注意:ceilf()对于确保图像质量在像素边界上很重要。

假设您希望文本和图像都水平居中,则图像位于文本上方:将界面生成器中的文本置于中间,并添加顶部插入(为图像腾出空间)。 (左边的插入为0)。 使用界面生成器来select图像 – 它的实际位置将从代码中设置,所以不要担心在IB中看起来不好。 不同于上面的其他答案,这实际上适用于所有当前支持的ios版本(5,6和7)。

在代码中,抓取图像后,只需放弃该button的ImageView(通过设置button的图像为空)(这也将自动居中文本,如果需要包装)。 然后用相同的帧大小和图像实例化自己的ImageView,并将其放置在中间。

这样,您仍然可以从界面构build器中select图像(虽然它不会像在模拟器中一样在IB中alignment,但是其他解决scheme在所有支持的ios版本中都不兼容)

我正在努力完成这个任务,因为我无法在视图的构造函数中获取图像大小和文本宽度。 Jesse的答案有两个小小的改变对我有效:

 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); 

变化是:

  • 使用[NSString sizeWithAttributes]获取文本宽度;
  • 直接在UIImage而不是UIImageView上获取图像大小

这适用于我,为几个button,不同的图像宽度和不同的标题长度:

子类UIButton

 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) } } 

适用于button尺寸80×80像素的罚款。

 [self.leftButton setImageEdgeInsets:UIEdgeInsetsMake(0, 10.0, 20.0, 10.0)]; [self.leftButton setTitleEdgeInsets:UIEdgeInsetsMake(60, -75.0, 0.0, 0.0)]; 

我做了一些调整,使图像中心水平alignment:

 // 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 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 } } 

您需要按照文本的宽度向右移动图像。 然后将文本左移图像的宽度。

 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; 

然后调整顶部和底部的插图来调整Y轴。 这可能也可以通过编程来完成,但对于图像大小应该是不变的。 而X轴插入将需要根据每个button中文本标签的大小进行更改。