将UIButton上的文本和图像与imageEdgeInsets和titleEdgeInsetsalignment

我想在文本的两行左边放置一个图标,使图像和文本的开头之间有大约2-3个像素的空间。 控件本身是水平alignment的(通过Interface Builder设置)

button会像这样:

| | |[Image] Add To | | Favorites | 

我试图configuration这个contentEdgeInset,imageEdgeInsets和titleEdgeInsets无济于事。 我知道一个负值会扩大边缘,而正值会缩小边缘,使其更靠近中心。

我试过了:

 [button setTitleEdgeInsets:UIEdgeInsetsMake(0, -image.size.width, 0, 0)]; [button setImageEdgeInsets:UIEdgeInsetsMake(0, button.titleLabel.bounds.size.width, 0, 0)]; 

但是这并不能正确显示。 我一直在调整值,但从左边的插图值-5到-10,似乎没有以预期的方式移动它。 -10会将文字一路向左滑动,所以我预期-5会从左侧向前滑动一半,但不会。

插入内容的逻辑是什么? 我不熟悉图片展示位置和相关术语。

我用这个问题作为参考,但关于我的价值观是不正确的。 UIButton:如何使用imageEdgeInsets和titleEdgeInsets居中图像和文本?

我同意在imageEdgeInsetstitleEdgeInsets上的文档应该更好,但我想出了如何得到正确的定位,而不诉诸试验和错误。

总的想法是在这个问题上 ,但这是如果你想文本和图像的中心。 我们不希望图像和文本单独居中,我们希望图像和文本作为一个整体集中在一起。 这实际上是UIButton已经做的,所以我们只需要调整间距。

 CGFloat spacing = 10; // the amount of spacing to appear between image and title tabBtn.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, spacing); tabBtn.titleEdgeInsets = UIEdgeInsetsMake(0, spacing, 0, 0); 

我也把它变成了UIButton的一个类别,所以它很容易使用:

的UIButton + Position.h

 @interface UIButton(ImageTitleCentering) -(void) centerButtonAndImageWithSpacing:(CGFloat)spacing; @end 

的UIButton + Position.m

 @implementation UIButton(ImageTitleCentering) -(void) centerButtonAndImageWithSpacing:(CGFloat)spacing { self.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, spacing); self.titleEdgeInsets = UIEdgeInsetsMake(0, spacing, 0, 0); } @end 

所以我现在要做的就是:

 [button centerButtonAndImageWithSpacing:10]; 

我每次都得到我需要的东西。 没有更多的手动边缘插入搞乱。

编辑:交换图像和文本

回复@Javal的评论

使用相同的机制,我们可以交换图像和文本。 要完成交换,只需使用负面间距,但也包括文本和图像的宽度。 这将需要帧已知和布局已经执行。

 [self.view layoutIfNeeded]; CGFloat flippedSpacing = -(desiredSpacing + button.currentImage.size.width + button.titleLabel.frame.size.width); [button centerButtonAndImageWithSpacing:flippedSpacing]; 

当然,你可能会想为此做一个很好的方法,可能会增加第二类方法,这是读者的一个练习。

我对这个派对有点迟了,但是我觉得我有一些有用的补充。

Kekoa的回答非常好,但正如RonLugge所说的那样,它可以使button不再尊重sizeToFit或者更重要的是,当button的大小固定时,可以使button剪裁它的内容。 哎呀!

首先,虽然,

我相信imageEdgeInsetstitleEdgeInsets工作原理的简短说明:

imageEdgeInsets的文档部分说明如下:

使用此属性来调整和重新定位button图像的有效绘图矩形。 您可以为四个插图(顶部,左侧,底部,右侧)中的每一个指定不同的值。 一个正值缩小,或插入,边缘移动它靠近button的中心。 负值会扩大或突破这一边缘。

我相信这个文档是想象button没有标题,只是一个图像。 这样做更有意义,并且performanceUIEdgeInsets通常如何做。 基本上,图像的框架(或titleEdgeInsets的标题)向内移动为正插入,向外移入负插入。

好的,那又怎么样?

我快到那里了! 以下是默认设置,设置图像和标题(button边框为绿色以显示其位置):

开始形象;标题和图像之间没有空格。

当你想要一个图像和一个标题之间的间隔,而不会导致任何被粉碎,你需要设置四个不同的插图,每个图像和标题两个。 那是因为你不想改变这些元素的大小 ,而只是改变他们的位置。 当你开始这样思考的时候,Kekoa优秀类别所需要的变化就变得清晰了:

 @implementation UIButton(ImageTitleCentering) - (void)centerButtonAndImageWithSpacing:(CGFloat)spacing { CGFloat insetAmount = spacing / 2.0; self.imageEdgeInsets = UIEdgeInsetsMake(0, -insetAmount, 0, insetAmount); self.titleEdgeInsets = UIEdgeInsetsMake(0, insetAmount, 0, -insetAmount); } @end 

但是等等 ,你说, 当我这样做时,我得到这个:

间距很好,但图像和标题不在视图框架之内。

哦耶! 我忘了, 文件警告我这个。 他们说,部分:

此属性仅用于在布局过程中定位图像。 该button不使用此属性来确定intrinsicContentSizesizeThatFits:

但有一个属性可以帮助,那就是contentEdgeInsets 。 这个文件部分地说:

该button使用此属性来确定intrinsicContentSizesizeThatFits:

听起来不错。 所以我们再来调整一下类别:

 @implementation UIButton(ImageTitleCentering) - (void)centerButtonAndImageWithSpacing:(CGFloat)spacing { CGFloat insetAmount = spacing / 2.0; self.imageEdgeInsets = UIEdgeInsetsMake(0, -insetAmount, 0, insetAmount); self.titleEdgeInsets = UIEdgeInsetsMake(0, insetAmount, 0, -insetAmount); self.contentEdgeInsets = UIEdgeInsetsMake(0, insetAmount, 0, insetAmount); } @end 

你会得到什么?

间距和框架现在是正确的。

看起来像我的赢家。


在斯威夫特工作,不想做任何思考? 以下是Swift扩展的最终版本:

 extension UIButton { func centerTextAndImage(spacing: CGFloat) { let insetAmount = spacing / 2 imageEdgeInsets = UIEdgeInsets(top: 0, left: -insetAmount, bottom: 0, right: insetAmount) titleEdgeInsets = UIEdgeInsets(top: 0, left: insetAmount, bottom: 0, right: -insetAmount) contentEdgeInsets = UIEdgeInsets(top: 0, left: insetAmount, bottom: 0, right: insetAmount) } } 

在界面生成器。 selectUIButton – > Attributes Inspector – > Edge = Title并修改边缘插页

另外如果你想做出类似的东西

在这里输入图像说明

你需要

1.将button的水平和垂直alignment设置为

在这里输入图像说明

  1. find所有需要的值并设置UIImageEdgeInsets

      CGSize buttonSize = button.frame.size; NSString *buttonTitle = button.titleLabel.text; CGSize titleSize = [buttonTitle sizeWithAttributes:@{ NSFontAttributeName : [UIFont camFontZonaProBoldWithSize:12.f] }]; UIImage *buttonImage = button.imageView.image; CGSize buttonImageSize = buttonImage.size; CGFloat offsetBetweenImageAndText = 10; //vertical space between image and text [button setImageEdgeInsets:UIEdgeInsetsMake((buttonSize.height - (titleSize.height + buttonImageSize.height)) / 2 - offsetBetweenImageAndText, (buttonSize.width - buttonImageSize.width) / 2, 0,0)]; [button setTitleEdgeInsets:UIEdgeInsetsMake((buttonSize.height - (titleSize.height + buttonImageSize.height)) / 2 + buttonImageSize.height + offsetBetweenImageAndText, titleSize.width + [button imageEdgeInsets].left > buttonSize.width ? -buttonImage.size.width + (buttonSize.width - titleSize.width) / 2 : (buttonSize.width - titleSize.width) / 2 - buttonImage.size.width, 0,0)]; 

这将安排你的标题和图像button。

另请注意每个重新布置更新


迅速

 import UIKit extension UIButton { // MARK: - UIButton+Aligment func alignContentVerticallyByCenter(offset:CGFloat = 10) { let buttonSize = frame.size if let titleLabel = titleLabel, let imageView = imageView { if let buttonTitle = titleLabel.text, let image = imageView.image { let titleString:NSString = NSString(string: buttonTitle) let titleSize = titleString.sizeWithAttributes([ NSFontAttributeName : titleLabel.font ]) let buttonImageSize = image.size let topImageOffset = (buttonSize.height - (titleSize.height + buttonImageSize.height + offset)) / 2 let leftImageOffset = (buttonSize.width - buttonImageSize.width) / 2 imageEdgeInsets = UIEdgeInsetsMake(topImageOffset, leftImageOffset, 0,0) let titleTopOffset = topImageOffset + offset + buttonImageSize.height let leftTitleOffset = (buttonSize.width - titleSize.width) / 2 - image.size.width titleEdgeInsets = UIEdgeInsetsMake(titleTopOffset, leftTitleOffset, 0,0) } } } } 

你可以避免使用这个麻烦 –

 myButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft; myButton.contentVerticalAlignment = UIControlContentVerticalAlignment; 

这将自动将所有内容alignment到左侧(或任何您想要的位置)

Swift 3:

 myButton.contentHorizontalAlignment = UIControlContentHorizontalAlignment.left; myButton.contentVerticalAlignment = UIControlContentVerticalAlignment.center; 

我也有点迟到这个派对,但我觉得我有一些有用的补充:o)。

我创build了一个UIButton子类,其目的是能够select垂直或水平button的图像布局。

这意味着你可以做这种button: 不同种类的按钮

这里有关于如何用我的类创build这些button的细节:

 func makeButton (imageVerticalAlignment:LayoutableButton.VerticalAlignment, imageHorizontalAlignment:LayoutableButton.HorizontalAlignment, title:String) -> LayoutableButton { let button = LayoutableButton () button.imageVerticalAlignment = imageVerticalAlignment button.imageHorizontalAlignment = imageHorizontalAlignment button.setTitle(title, for: .normal) // add image, border, ... return button } let button1 = makeButton(imageVerticalAlignment: .center, imageHorizontalAlignment: .left, title: "button1") let button2 = makeButton(imageVerticalAlignment: .center, imageHorizontalAlignment: .right, title: "button2") let button3 = makeButton(imageVerticalAlignment: .top, imageHorizontalAlignment: .center, title: "button3") let button4 = makeButton(imageVerticalAlignment: .bottom, imageHorizontalAlignment: .center, title: "button4") let button5 = makeButton(imageVerticalAlignment: .bottom, imageHorizontalAlignment: .center, title: "button5") button5.contentEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) 

为此,我添加了2个属性: imageVerticalAlignmentimageHorizontalAlignment 。 当然,如果你的button只有一个图像或标题…不要使用这个类!

我还添加了一个名为imageToTitleSpacing的属性,它允许您调整标题和图像之间的空间。

如果您想直接使用imageEdgeInsetstitleEdgeInsetscontentEdgeInsets ,或者与新的布局属性组合使用,则该类尽量兼容。

正如@ravron解释我们,我尽我所能使button内容边缘正确(正如你可以看到红色边框)。

您也可以在Interface Builder中使用它:

  1. 创build一个UIButton
  2. 更改button类
  3. 使用“中心”,“顶部”,“底部”,“左侧”或“右侧”来调整布局属性 按钮属性

这里的代码( gist ):

 @IBDesignable class LayoutableButton: UIButton { enum VerticalAlignment : String { case center, top, bottom, unset } enum HorizontalAlignment : String { case center, left, right, unset } @IBInspectable var imageToTitleSpacing: CGFloat = 8.0 { didSet { setNeedsLayout() } } var imageVerticalAlignment: VerticalAlignment = .unset { didSet { setNeedsLayout() } } var imageHorizontalAlignment: HorizontalAlignment = .unset { didSet { setNeedsLayout() } } @available(*, unavailable, message: "This property is reserved for Interface Builder. Use 'imageVerticalAlignment' instead.") @IBInspectable var imageVerticalAlignmentName: String { get { return imageVerticalAlignment.rawValue } set { if let value = VerticalAlignment(rawValue: newValue) { imageVerticalAlignment = value } else { imageVerticalAlignment = .unset } } } @available(*, unavailable, message: "This property is reserved for Interface Builder. Use 'imageHorizontalAlignment' instead.") @IBInspectable var imageHorizontalAlignmentName: String { get { return imageHorizontalAlignment.rawValue } set { if let value = HorizontalAlignment(rawValue: newValue) { imageHorizontalAlignment = value } else { imageHorizontalAlignment = .unset } } } var extraContentEdgeInsets:UIEdgeInsets = UIEdgeInsets.zero override var contentEdgeInsets: UIEdgeInsets { get { return super.contentEdgeInsets } set { super.contentEdgeInsets = newValue self.extraContentEdgeInsets = newValue } } var extraImageEdgeInsets:UIEdgeInsets = UIEdgeInsets.zero override var imageEdgeInsets: UIEdgeInsets { get { return super.imageEdgeInsets } set { super.imageEdgeInsets = newValue self.extraImageEdgeInsets = newValue } } var extraTitleEdgeInsets:UIEdgeInsets = UIEdgeInsets.zero override var titleEdgeInsets: UIEdgeInsets { get { return super.titleEdgeInsets } set { super.titleEdgeInsets = newValue self.extraTitleEdgeInsets = newValue } } //Needed to avoid IB crash during autolayout override init(frame: CGRect) { super.init(frame: frame) } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.imageEdgeInsets = super.imageEdgeInsets self.titleEdgeInsets = super.titleEdgeInsets self.contentEdgeInsets = super.contentEdgeInsets } override func layoutSubviews() { if let imageSize = self.imageView?.image?.size, let font = self.titleLabel?.font, let textSize = self.titleLabel?.attributedText?.size() ?? self.titleLabel?.text?.size(attributes: [NSFontAttributeName: font]) { var _imageEdgeInsets = UIEdgeInsets.zero var _titleEdgeInsets = UIEdgeInsets.zero var _contentEdgeInsets = UIEdgeInsets.zero let halfImageToTitleSpacing = imageToTitleSpacing / 2.0 switch imageVerticalAlignment { case .bottom: _imageEdgeInsets.top = (textSize.height + imageToTitleSpacing) / 2.0 _imageEdgeInsets.bottom = (-textSize.height - imageToTitleSpacing) / 2.0 _titleEdgeInsets.top = (-imageSize.height - imageToTitleSpacing) / 2.0 _titleEdgeInsets.bottom = (imageSize.height + imageToTitleSpacing) / 2.0 _contentEdgeInsets.top = (min (imageSize.height, textSize.height) + imageToTitleSpacing) / 2.0 _contentEdgeInsets.bottom = (min (imageSize.height, textSize.height) + imageToTitleSpacing) / 2.0 //only works with contentVerticalAlignment = .center contentVerticalAlignment = .center case .top: _imageEdgeInsets.top = (-textSize.height - imageToTitleSpacing) / 2.0 _imageEdgeInsets.bottom = (textSize.height + imageToTitleSpacing) / 2.0 _titleEdgeInsets.top = (imageSize.height + imageToTitleSpacing) / 2.0 _titleEdgeInsets.bottom = (-imageSize.height - imageToTitleSpacing) / 2.0 _contentEdgeInsets.top = (min (imageSize.height, textSize.height) + imageToTitleSpacing) / 2.0 _contentEdgeInsets.bottom = (min (imageSize.height, textSize.height) + imageToTitleSpacing) / 2.0 //only works with contentVerticalAlignment = .center contentVerticalAlignment = .center case .center: //only works with contentVerticalAlignment = .center contentVerticalAlignment = .center break case .unset: break } switch imageHorizontalAlignment { case .left: _imageEdgeInsets.left = -halfImageToTitleSpacing _imageEdgeInsets.right = halfImageToTitleSpacing _titleEdgeInsets.left = halfImageToTitleSpacing _titleEdgeInsets.right = -halfImageToTitleSpacing _contentEdgeInsets.left = halfImageToTitleSpacing _contentEdgeInsets.right = halfImageToTitleSpacing case .right: _imageEdgeInsets.left = textSize.width + halfImageToTitleSpacing _imageEdgeInsets.right = -textSize.width - halfImageToTitleSpacing _titleEdgeInsets.left = -imageSize.width - halfImageToTitleSpacing _titleEdgeInsets.right = imageSize.width + halfImageToTitleSpacing _contentEdgeInsets.left = halfImageToTitleSpacing _contentEdgeInsets.right = halfImageToTitleSpacing case .center: _imageEdgeInsets.left = textSize.width / 2.0 _imageEdgeInsets.right = -textSize.width / 2.0 _titleEdgeInsets.left = -imageSize.width / 2.0 _titleEdgeInsets.right = imageSize.width / 2.0 _contentEdgeInsets.left = -((imageSize.width + textSize.width) - max (imageSize.width, textSize.width)) / 2.0 _contentEdgeInsets.right = -((imageSize.width + textSize.width) - max (imageSize.width, textSize.width)) / 2.0 case .unset: break } _contentEdgeInsets.top += extraContentEdgeInsets.top _contentEdgeInsets.bottom += extraContentEdgeInsets.bottom _contentEdgeInsets.left += extraContentEdgeInsets.left _contentEdgeInsets.right += extraContentEdgeInsets.right _imageEdgeInsets.top += extraImageEdgeInsets.top _imageEdgeInsets.bottom += extraImageEdgeInsets.bottom _imageEdgeInsets.left += extraImageEdgeInsets.left _imageEdgeInsets.right += extraImageEdgeInsets.right _titleEdgeInsets.top += extraTitleEdgeInsets.top _titleEdgeInsets.bottom += extraTitleEdgeInsets.bottom _titleEdgeInsets.left += extraTitleEdgeInsets.left _titleEdgeInsets.right += extraTitleEdgeInsets.right super.imageEdgeInsets = _imageEdgeInsets super.titleEdgeInsets = _titleEdgeInsets super.contentEdgeInsets = _contentEdgeInsets } else { super.imageEdgeInsets = extraImageEdgeInsets super.titleEdgeInsets = extraTitleEdgeInsets super.contentEdgeInsets = extraContentEdgeInsets } super.layoutSubviews() } } 

Xcode 8.0中,您可以通过更改大小检查器中的insets来简单地执行此操作。

selectUIButton – > Attributes Inspector – >去检查大小,并修改内容,图像和标题的插图。

在这里输入图像说明

如果你想改变右侧的图像,你可以简单地在属性检查器中将语义属性更改为Force Right-to-left

在这里输入图像说明

对Riley Avron的一个小补充回答了帐户区域设置更改:

 extension UIButton { func centerTextAndImage(spacing: CGFloat) { let insetAmount = spacing / 2 let writingDirection = UIApplication.sharedApplication().userInterfaceLayoutDirection let factor: CGFloat = writingDirection == .LeftToRight ? 1 : -1 self.imageEdgeInsets = UIEdgeInsets(top: 0, left: -insetAmount*factor, bottom: 0, right: insetAmount*factor) self.titleEdgeInsets = UIEdgeInsets(top: 0, left: insetAmount*factor, bottom: 0, right: -insetAmount*factor) self.contentEdgeInsets = UIEdgeInsets(top: 0, left: insetAmount, bottom: 0, right: insetAmount) } } 

这里是一个简单的例子,如何使用imageEdgeInsets这将使一个30x30button的可触及面积10像素一路大(50×50)

  var expandHittableAreaAmt : CGFloat = 10 var buttonWidth : CGFloat = 30 var button = UIButton.buttonWithType(UIButtonType.Custom) as UIButton button.frame = CGRectMake(0, 0, buttonWidth+expandHittableAreaAmt, buttonWidth+expandHittableAreaAmt) button.imageEdgeInsets = UIEdgeInsetsMake(expandHittableAreaAmt, expandHittableAreaAmt, expandHittableAreaAmt, expandHittableAreaAmt) button.setImage(UIImage(named: "buttonImage"), forState: .Normal) button.addTarget(self, action: "didTouchButton:", forControlEvents:.TouchUpInside) 

Swift 3中的一个优雅的方式,更好理解:

 override func imageRect(forContentRect contentRect: CGRect) -> CGRect { let leftMargin:CGFloat = 40 let imgWidth:CGFloat = 24 let imgHeight:CGFloat = 24 return CGRect(x: leftMargin, y: (contentRect.size.height-imgHeight) * 0.5, width: imgWidth, height: imgHeight) } override func titleRect(forContentRect contentRect: CGRect) -> CGRect { let leftMargin:CGFloat = 80 let rightMargin:CGFloat = 80 return CGRect(x: leftMargin, y: 0, width: contentRect.size.width-leftMargin-rightMargin, height: contentRect.size.height) } override func backgroundRect(forBounds bounds: CGRect) -> CGRect { let leftMargin:CGFloat = 10 let rightMargin:CGFloat = 10 let topMargin:CGFloat = 10 let bottomMargin:CGFloat = 10 return CGRect(x: leftMargin, y: topMargin, width: bounds.size.width-leftMargin-rightMargin, height: bounds.size.height-topMargin-bottomMargin) } override func contentRect(forBounds bounds: CGRect) -> CGRect { let leftMargin:CGFloat = 5 let rightMargin:CGFloat = 5 let topMargin:CGFloat = 5 let bottomMargin:CGFloat = 5 return CGRect(x: leftMargin, y: topMargin, width: bounds.size.width-leftMargin-rightMargin, height: bounds.size.height-topMargin-bottomMargin) }