ios 使用 imageEdgeInsets 和 titleEdgeInsets 对齐 UIButton 上的文本和图像

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4564621/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 18:24:56  来源:igfitidea点击:

Aligning text and image on UIButton with imageEdgeInsets and titleEdgeInsets

iphoneiosuibuttonuikit

提问by Justin Galzic

I would like to place an icon left of the two lines of text such that there's about 2-3 pixels of space between the image and the start of text. The control itself is Center aligned horizontally (set through Interface Builder)

我想在两行文本的左侧放置一个图标,以便图像和文本开头之间有大约 2-3 个像素的空间。控件本身水平居中对齐(通过界面生成器设置)

The button would resemble something like this:

该按钮将类似于以下内容:

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

I'm trying to configure this with contentEdgeInset, imageEdgeInsets and titleEdgeInsets to no avail. I understand that a negative value expands the edge while a positive value shrinks it to move it closer to the center.

我正在尝试使用 contentEdgeInset、imageEdgeInsets 和 titleEdgeInsets 对其进行配置,但无济于事。我知道负值会扩大边缘,而正值会缩小边缘以使其更靠近中心。

I tried:

我试过:

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

but this doesn't display it correctly. I've been tweaking the values but going from say -5 to -10 on the left inset value doesn't appear to move it in expected manner. -10 will scoot the text all the way to the left so I expected -5 to scoot it half way from the left side but it doesn't.

但这不能正确显示。我一直在调整这些值,但是从左边插入值的 -5 到 -10 似乎并没有以预期的方式移动它。-10 会将文本一直向左移动,所以我希望 -5 将它从左侧移动到一半,但事实并非如此。

What's the logic behind insets? I'm not familiar with image placements and related terminology.

插入背后的逻辑是什么?我不熟悉图像放置和相关术语。

I used this SO question as a reference but something about my values isn't right. UIButton: how to center an image and a text using imageEdgeInsets and titleEdgeInsets?

我使用this SO question作为参考,但我的价值观有些不对。 UIButton:如何使用 imageEdgeInsets 和 titleEdgeInsets 将图像和文本居中?

回答by Kekoa

I agree the documentation on imageEdgeInsetsand titleEdgeInsetsshould be better, but I figured out how to get the correct positioning without resorting to trial and error.

我同意文档imageEdgeInsets并且titleEdgeInsets应该更好,但我想出了如何在不诉诸试验和错误的情况下获得正确的定位。

The general idea is here at this question, but that was if you wanted both text and image centered. We don't want the image and text to be centered individually, we want the image and the text to be centered together as a single entity. This is in fact what UIButton already does so we simply need to adjust the spacing.

这个问题的一般想法在这里,但如果你想要文本和图像都居中。我们不希望图像和文本单独居中,我们希望图像和文本作为单个实体一起居中。这实际上是 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);

I also turned this into a category for UIButton so it will be easy to use:

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

UIButton+Position.h

UIButton+Position.h

@interface UIButton(ImageTitleCentering)

-(void) centerButtonAndImageWithSpacing:(CGFloat)spacing;

@end

UIButton+Position.m

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

So now all I have to do is:

所以现在我要做的就是:

[button centerButtonAndImageWithSpacing:10];

And I get what I need every time. No more messing with the edge insets manually.

我每次都能得到我需要的东西。不再手动弄乱边缘插入物。

EDIT: Swapping Image and Text

编辑:交换图像和文本

In response to @Javal in comments

在评论中回应@Javal

Using this same mechanism, we can swap the image and the text. To accomplish the swap, simply use a negative spacing but also include the width of the text and the image. This will require frames to be known and layout performed already.

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

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

Of course you will probably want to make a nice method for this, potentially adding a second category method, this is left as an exercise to the reader.

当然,您可能希望为此创建一个很好的方法,可能会添加第二类方法,这留给读者作为练习。

回答by ravron

I'm a little late to this party, but I think I have something useful to add.

我参加这个派对有点晚了,但我想我有一些有用的东西要补充。

Kekoa's answer is great but, as RonLugge mentions, it can make the button no longer respect sizeToFitor, more importantly, can cause the button to clip its content when it is intrinsically sized. Yikes!

Kekoa 的回答很好,但是,正如 RonLugge 所提到的,它可以使按钮不再受尊重,sizeToFit或者更重要的是,当按钮本身具有大小时,它会导致其内容被剪裁。哎呀!

First, though,

不过首先,

A brief explanation of how I believe imageEdgeInsetsand titleEdgeInsetswork:

简要说明我的信仰imageEdgeInsetstitleEdgeInsets工作方式:

The docs for imageEdgeInsetshave the following to say, in part:

对文档imageEdgeInsets有以下的说法,部分内容如下:

Use this property to resize and reposition the effective drawing rectangle for the button image. You can specify a different value for each of the four insets (top, left, bottom, right). A positive value shrinks, or insets, that edge—moving it closer to the center of the button. A negative value expands, or outsets, that edge.

使用此属性可以调整按钮图像的有效绘图矩形的大小和位置。您可以为四个插图(顶部、左侧、底部、右侧)中的每一个指定不同的值。正值会缩小或插入该边缘 - 将其移近按钮的中心。负值会扩展或开始该边缘。

I believe that this documentation was written imagining that the button has no title, just an image. It makes a lot more sense thought of this way, and behaves how UIEdgeInsetsusually do. Basically, the frame of the image (or the title, with titleEdgeInsets) is moved inwards for positive insets and outwards for negative insets.

我相信这个文档是在想象按钮没有标题,只有一个图像的情况下编写的。以这种方式思考更有意义,并且表现得像往常UIEdgeInsets一样。基本上,图像的框架(或标题,带有titleEdgeInsets)向内移动以表示正插图,向外移动表示负插图。

OK, so what?

好的,那又怎样?

I'm getting there! Here's what you have by default, setting an image and a title (the button border is green just to show where it is):

我快到那里了!这是默认情况下,设置图像和标题(按钮边框为绿色只是为了显示它的位置):

Starting image; no space between title and image.

起始图像; 标题和图片之间没有空格。

When you want spacing between an image and a title, without causing either to be crushed, you need to set four different insets, two on each of the image and title. That's because you don't want to change the sizesof those elements' frames, but just their positions. When you start thinking this way, the needed change to Kekoa's excellent category becomes clear:

如果您希望在图像和标题之间留出间距,而不会导致任何一个被压碎,您需要设置四个不同的插图,每个图像和标题上都有两个。那是因为您不想更改这些元素框架的大小,而只想更改它们的位置。当你开始这样思考时,对 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

But wait, you say, when I do that, I get this:

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

Spacing is good, but the image and title are outside the view's frame.

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

Oh yeah! I forgot, the docswarned me about this. They say, in part:

哦耶!我忘了,文档警告过我这一点。他们说,部分:

This property is used only for positioning the image during layout. The button does not use this property to determine intrinsicContentSizeand sizeThatFits:.

此属性仅用于在布局期间定位图像。该按钮不使用此属性来确定intrinsicContentSizesizeThatFits:

But there isa property that can help, and that's contentEdgeInsets. The docsfor that say, in part:

但有一个属性,它可以帮助,这就是contentEdgeInsets文档部分说:

The button uses this property to determine intrinsicContentSizeand sizeThatFits:.

该按钮使用此属性来确定intrinsicContentSizesizeThatFits:

That sounds good. So let's tweak the category once more:

听起来不错。因此,让我们再次调整类别:

@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

And what do you get?

你会得到什么?

Spacing and frame are now correct.

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

Looks like a winner to me.

对我来说似乎是赢家。



Working in Swift and don't want to do any thinking at all? Here's the final version of the extension in Swift:

在 Swift 工作,根本不想做任何思考?这是 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)
    }
}

回答by Freeman

In interface Builder. Select the UIButton -> Attributes Inspector -> Edge=Title and modify the edge insets

在界面生成器中。选择 UIButton -> Attributes Inspector -> Edge=Title 并修改边缘插入

回答by gbk

Also if you want to make something similar to

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

enter image description here

在此处输入图片说明

You need

你需要

1.Set horizontal and vertical alignment for button to

1.设置按钮的水平和垂直对齐方式

enter image description here

在此处输入图片说明

  1. Find all required values and set 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)];
    
  1. 找到所有需要的值并设置 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)];
    

This will arrange your title and image on button.

这将在按钮上排列您的标题和图像。

Also please note update this on each relayout

另请注意在每次重新布局时更新此内容



Swift

迅速

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

回答by Nishant

You can avoid much trouble by using this --

使用这个可以避免很多麻烦——

myButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;   
myButton.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;

This will align all your content automatically to left (or wherever you want it)

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

Swift 3:

斯威夫特 3:

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

回答by Sahil

In Xcode 8.0you can simply do it by changing insetsin size inspector.

Xcode 8.0 中,您可以通过更改insets大小检查器来简单地做到这一点。

Select the UIButton -> Attributes Inspector -> go to size inspector and modify the content, image and title insets.

选择 UIButton -> Attributes Inspector -> go to size inspector 并修改内容、图像和标题插入。

enter image description here

在此处输入图片说明

And if you want to change image on right side you can simply change the semantic property to Force Right-to-leftin Attribute inspector .

如果您想更改右侧的图像,您可以简单地将语义属性更改为Force Right-to-leftin Attribute inspector 。

enter image description here

在此处输入图片说明

回答by gbitaudeau

I'm a little late to this party too, but I think I have something useful to add :o).

我参加这个聚会也有点晚了,但我想我有一些有用的东西要补充:o)。

I created a UIButtonsubclass whose purpose is to be able to choose where the button's image is layout, either vertically or horizontally.

我创建了一个UIButton子类,其目的是能够选择按钮图像的布局位置,垂直或水平。

It means that you can make this kind of buttons : different kind of buttons

这意味着您可以制作这种按钮: 不同种类的按钮

Here the details about how to create these buttons with my class :

这是有关如何使用我的课程创建这些按钮的详细信息:

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)

To do that, I added 2 attributes : imageVerticalAlignmentand imageHorizontalAlignment. Off course, If your button only have an image or a title ... don't use this class at all !

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

I also added an attribute named imageToTitleSpacingwhich allow you to adjust space between title and image.

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

This class try his best to be compatible if you want to use imageEdgeInsets, titleEdgeInsetsand contentEdgeInsetsdirectly or in combinaison with the new layout attributes.

这个类尽量兼容,如果你想直接使用imageEdgeInsets,titleEdgeInsetscontentEdgeInsets直接或结合新的布局属性。

As @ravron explains us, I try my best to make the button content edge correct (as you can see with the red borders).

正如@ravron 向我们解释的那样,我尽力使按钮内容边缘正确(如红色边框所示)。

You can also use it in Interface Builder :

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

  1. Create a UIButton
  2. Change the button class
  3. Adjust Layoutable Attributes using "center", "top", "bottom", "left" or "right" button attributes
  1. 创建一个 UIButton
  2. 更改按钮类
  3. 使用“center”、“top”、“bottom”、“left”或“right”调整可布局属性 按钮属性

Here the code (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()
    }
}

回答by orxelm

A small addition to Riley Avron answer to account locale changes:

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

回答by Hemang

Swift 4.x

斯威夫特 4.x

extension UIButton {
    func centerTextAndImage(spacing: CGFloat) {
        let insetAmount = spacing / 2
        let writingDirection = UIApplication.shared.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)
    }
}

Usage:

用法

button.centerTextAndImage(spacing: 10.0)

回答by Jules

I write code bewlow. It works well in product version. Supprot Swift 4.2 +

我在下面写代码。它在产品版本中运行良好。Supprot雨燕4.2 +

extension UIButton{
 enum ImageTitleRelativeLocation {
    case imageUpTitleDown
    case imageDownTitleUp
    case imageLeftTitleRight
    case imageRightTitleLeft
}
 func centerContentRelativeLocation(_ relativeLocation: 
                                      ImageTitleRelativeLocation,
                                   spacing: CGFloat = 0) {
    assert(contentVerticalAlignment == .center,
           "only works with contentVerticalAlignment = .center !!!")

    guard (title(for: .normal) != nil) || (attributedTitle(for: .normal) != nil) else {
        assert(false, "TITLE IS NIL! SET TITTLE FIRST!")
        return
    }

    guard let imageSize = self.currentImage?.size else {
        assert(false, "IMGAGE IS NIL! SET IMAGE FIRST!!!")
        return
    }
    guard let titleSize = titleLabel?
        .systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) else {
            assert(false, "TITLELABEL IS NIL!")
            return
    }

    let horizontalResistent: CGFloat
    // extend contenArea in case of title is shrink
    if frame.width < titleSize.width + imageSize.width {
        horizontalResistent = titleSize.width + imageSize.width - frame.width
        print("horizontalResistent", horizontalResistent)
    } else {
        horizontalResistent = 0
    }

    var adjustImageEdgeInsets: UIEdgeInsets = .zero
    var adjustTitleEdgeInsets: UIEdgeInsets = .zero
    var adjustContentEdgeInsets: UIEdgeInsets = .zero

    let verticalImageAbsOffset = abs((titleSize.height + spacing) / 2)
    let verticalTitleAbsOffset = abs((imageSize.height + spacing) / 2)

    switch relativeLocation {
    case .imageUpTitleDown:

        adjustImageEdgeInsets.top = -verticalImageAbsOffset
        adjustImageEdgeInsets.bottom = verticalImageAbsOffset
        adjustImageEdgeInsets.left = titleSize.width / 2 + horizontalResistent / 2
        adjustImageEdgeInsets.right = -titleSize.width / 2 - horizontalResistent / 2

        adjustTitleEdgeInsets.top = verticalTitleAbsOffset
        adjustTitleEdgeInsets.bottom = -verticalTitleAbsOffset
        adjustTitleEdgeInsets.left = -imageSize.width / 2 + horizontalResistent / 2
        adjustTitleEdgeInsets.right = imageSize.width / 2 - horizontalResistent / 2

        adjustContentEdgeInsets.top = spacing
        adjustContentEdgeInsets.bottom = spacing
        adjustContentEdgeInsets.left = -horizontalResistent
        adjustContentEdgeInsets.right = -horizontalResistent
    case .imageDownTitleUp:
        adjustImageEdgeInsets.top = verticalImageAbsOffset
        adjustImageEdgeInsets.bottom = -verticalImageAbsOffset
        adjustImageEdgeInsets.left = titleSize.width / 2 + horizontalResistent / 2
        adjustImageEdgeInsets.right = -titleSize.width / 2 - horizontalResistent / 2

        adjustTitleEdgeInsets.top = -verticalTitleAbsOffset
        adjustTitleEdgeInsets.bottom = verticalTitleAbsOffset
        adjustTitleEdgeInsets.left = -imageSize.width / 2 + horizontalResistent / 2
        adjustTitleEdgeInsets.right = imageSize.width / 2 - horizontalResistent / 2

        adjustContentEdgeInsets.top = spacing
        adjustContentEdgeInsets.bottom = spacing
        adjustContentEdgeInsets.left = -horizontalResistent
        adjustContentEdgeInsets.right = -horizontalResistent
    case .imageLeftTitleRight:
        adjustImageEdgeInsets.left = -spacing / 2
        adjustImageEdgeInsets.right = spacing / 2

        adjustTitleEdgeInsets.left = spacing / 2
        adjustTitleEdgeInsets.right = -spacing / 2

        adjustContentEdgeInsets.left = spacing
        adjustContentEdgeInsets.right = spacing
    case .imageRightTitleLeft:
        adjustImageEdgeInsets.left = titleSize.width + spacing / 2
        adjustImageEdgeInsets.right = -titleSize.width - spacing / 2

        adjustTitleEdgeInsets.left = -imageSize.width - spacing / 2
        adjustTitleEdgeInsets.right = imageSize.width + spacing / 2

        adjustContentEdgeInsets.left = spacing
        adjustContentEdgeInsets.right = spacing
    }

    imageEdgeInsets = adjustImageEdgeInsets
    titleEdgeInsets = adjustTitleEdgeInsets
    contentEdgeInsets = adjustContentEdgeInsets

    setNeedsLayout()
}
}