关于iPhone:将UIButton上的文本和图像与imageEdgeInsets和titleEdgeInsets对齐

Aligning text and image on UIButton with imageEdgeInsets and titleEdgeInsets

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

该按钮类似于以下内容:

1
2
3
|                  |
|[Image] Add To    |
|        Favorites |

我正在尝试将contentEdgeInset,imageEdgeInsets和titleEdgeInsets配置为无效。我知道负值会扩大边缘,而正值会缩小边缘以使其更靠近中心。

我试过了:

1
2
[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将图像和文本居中?


我参加这个聚会有点晚了,但是我想补充一下。

Kekoa的答案很好,但是,正如RonLugge提到的那样,它可以使按钮不再尊重sizeToFit,或者更重要的是,可以使按钮在固有大小时裁剪其内容。 kes!

首先,

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

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.

我相信编写此文档时会想到按钮没有标题,只有图像。想到这种方式更有意义,并且表现出UIEdgeInsets通常的行为。基本上,图像的框(或标题为titleEdgeInsets)向内移动为正向插图,向外移动为负向插图。

好那怎么了

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

Starting image; no space between title and image.

如果要在图像和标题之间留出间隔,而又不致使两者粉碎,则需要设置四个不同的插图,在图像和标题上分别设置两个。那是因为您不想更改这些元素框架的大小,而只是更改其位置。当您开始以这种方式思考时,对Kekoa优秀类别的必要更改就很清楚了:

1
2
3
4
5
6
7
8
9
@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

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

Spacing is good, but the image and title are outside the views frame.

哦耶!我忘记了,文档对此警告了我。他们说:

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

但是有一个属性可以提供帮助,即contentEdgeInsets。该文档部分说:

The button uses this property to determine intrinsicContentSize and sizeThatFits:.

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

1
2
3
4
5
6
7
8
9
10
@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

那你得到什么呢?

Spacing and frame are now correct.

看起来对我来说是赢家。

在Swift中工作,根本不想做任何思考吗?这是Swift中扩展的最终版本:

1
2
3
4
5
6
7
8
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)
    }
}


我同意imageEdgeInsetstitleEdgeInsets上的文档应该更好,但是我想出了如何在不依靠反复试验的情况下获得正确位置的方法。

这个问题的总体思路就在这里,但这就是您是否希望文本和图像都居中。我们不希望图像和文本分别居中,我们希望图像和文本作为单个实体一起居中。实际上,这正是UIButton所做的,因此我们只需要调整间距即可。

1
2
3
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

1
2
3
4
5
@interface UIButton(ImageTitleCentering)

-(void) centerButtonAndImageWithSpacing:(CGFloat)spacing;

@end

UIButton + Position.m

1
2
3
4
5
6
7
8
@implementation UIButton(ImageTitleCentering)

-(void) centerButtonAndImageWithSpacing:(CGFloat)spacing {
    self.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, spacing);
    self.titleEdgeInsets = UIEdgeInsetsMake(0, spacing, 0, 0);
}

@end

所以现在我要做的就是:

1
[button centerButtonAndImageWithSpacing:10];

而且我每次都能得到所需的东西。不再需要手动弄乱边缘插图。

编辑:交换图像和文本

回应@Javal的评论

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

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

当然,您可能会想为此做一个不错的方法,可能会添加第二类方法,这留给读者练习。


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


另外,如果您想做类似的事情

enter image description here

你需要

1.将按钮的水平和垂直对齐设置为

enter image description here

  • 查找所有必需的值并设置UIImageEdgeInsets

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
            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
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    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)
                }
            }
        }
    }

    使用此方法可以避免很多麻烦-

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

    这将自动将您所有的内容向左(或您希望的位置)对齐

    斯威夫特3:

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


    在Xcode 8.0中,只需更改大小检查器中的insets即可。

    选择UIButton-> Attributes Inspector->转到size inspector并修改内容,图像和标题插图。

    enter image description here

    而且,如果您想在右侧更改图像,只需在Attribute inspector中将语义属性更改为Force Right-to-left即可。

    enter image description here


    我也参加这个聚会有点晚,但是我想补充一下:o)。

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

    这意味着您可以进行以下按钮:
    different kind of buttons

    这里是有关如何在我的课堂上创建这些按钮的详细信息:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    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。当然,如果您的按钮仅包含图像或标题...根本不要使用此类!

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

    如果您想直接使用imageEdgeInsetstitleEdgeInsetscontentEdgeInsets或与新的布局属性结合使用,则该类将尽最大努力使其兼容。

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

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

  • 创建一个UIButton
  • 更改按钮类别
  • 使用"中心","顶部","底部","左"或"右"调整可布局属性button attributes
  • 这里的代码(要点):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    @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()
        }
    }


    莱利·阿夫隆(Riley Avron)对帐户语言环境更改的答案有一个小的补充:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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)
        }
    }

    斯威夫特4.x

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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)
        }
    }

    用法:

    1
    button.centerTextAndImage(spacing: 10.0)


    我写下面的代码。在产品版本中效果很好。 Supprot Swift 4.2 +

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    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()
    }
    }

    这是有关如何使用imageEdgeInsets的简单示例
    这将使一个30x30的按钮具有一个可击中区域,并且在整个过程中都会放大10像素(50x50)

    1
    2
    3
    4
    5
    6
    7
        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中的一种优雅方法,您可以更好地理解:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    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)
    }

    该解决方案的快速4.2版本如下所示:

    1
    2
    3
    let spacing: CGFloat = 10 // the amount of spacing to appear between image and title
    self.button?.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: spacing)
    self.button?.titleEdgeInsets = UIEdgeInsets(top: 0, left: spacing, bottom: 0, right: 0)