UIButtonのテキストと画像をimageEdgeInsetsとtitleEdgeInsetsに揃える


248

2行のテキストの左側にアイコンを配置して、画像とテキストの先頭の間に約2〜3ピクセルのスペースを空けたいと思います。コントロール自体は水平に中央揃えされます(Interface Builderで設定)

ボタンは次のようになります。

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

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は左側からスクートすることを期待していましたが、そうしませんでした。

インセットの背後にある論理は何ですか?画像の配置と関連する用語に精通していません。

私はこのSOの質問を参照として使用しましたが、私の値について何かが正しくありません。 UIButton:imageEdgeInsetsとtitleEdgeInsetsを使用して画像とテキストを中央に配置する方法?

回答:


392

私は上の文書に同意imageEdgeInsetsし、titleEdgeInsets良いはずですが、私は試行錯誤に頼らずに正しい位置を取得する方法を考え出しました。

一般的な考え方はこの質問にありますが、テキストと画像の両方を中央に配置する必要がある場合です。画像とテキストを個別に中央揃えにするのではなく、画像とテキストを1つのエンティティとして中央揃えにします。これは実際には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];

もちろん、おそらくこれに適したメソッドを作成し、2番目のカテゴリのメソッドを追加することもできます。これは、読者への課題として残しておきます。


通常のタイトルと強調表示のタイトルが異なる場合、ユーザーがボタンを強調表示して強調表示を解除したときに、タイトルを中央に戻すにはどうすればよいですか?
user102008

@ user102008完全に異なるタイトルですか?または単に異なる色ですか?を使用[UIButton setTitleColor:forState:]している場合でも、位置を変更する必要はありません[UIButton setTitle:forState:]
Kekoa

5
[ボタンのsizeToFit]をどのように修正すれば、適切なサイズになりますか?
RonLugge 2012

@RonLuggeなぜsizeToFitが必要なのかわからないので、お答えできません。sizeToFitを使用しなくても、問題なく動作します。
Kekoa 2012

ボタン/テキストは動的であるため、sizeToFitが必要なので、(ユーザー定義の)ラベルに合わせてボタンのサイズを変更する必要があります。問題は、追加されたスペースを補償しないことです。私はそれを上書きして、手動でフレームの幅を10増やしました
RonLugge

395

私はこのパーティーに少し遅れましたが、何か付け加えることがあると思います。

Kekoaの答えはすばらしいですが、RonLuggeが言及しているように、ボタンを尊重しなくなっsizeToFitたり、さらに重要なことに、ボタンのサイズが本来のサイズの場合にボタンがコンテンツをクリップする可能性があります。うわぁ!

最初に、しかし、

私がどのように信じimageEdgeInsetstitleEdgeInsets働くかについての簡単な説明:

ドキュメントにimageEdgeInsetsは、一部、次のように述べられています。

このプロパティを使用して、ボタン画像の効果的な描画長方形のサイズと位置を変更します。4つのインセット(上、左、下、右)のそれぞれに異なる値を指定できます。正の値を指定すると、そのエッジが縮小または挿入され、ボタンの中心に近づきます。負の値は、そのエッジを拡張またはアウトセットします。

このドキュメントは、ボタンにタイトルがなく、画像のみであることを想定して書かれたと思います。この方法を考えると、もっと理にかなっていて、UIEdgeInsets通常の動作をします。基本的に、画像のフレーム(またはの付いたタイトルtitleEdgeInsets)は、正のインセットの場合は内側に、負のインセットの場合は外側に移動します。

じゃあ何?

そろそろ!画像とタイトルを設定するデフォルトの設定は次のとおりです(ボタンの境界線は、それがどこにあるかを示すために緑色になっています)。

開始画像;  タイトルと画像の間にスペースはありません。

画像とタイトルの間にスペースを入れたい場合、どちらも押しつぶさないようにするには、画像とタイトルのそれぞれに2つずつ、4つの異なるインセットを設定する必要があります。これは、これらの要素のフレームのサイズを変更するのではなく、位置だけを変更したいためです。このように考え始めると、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

しかし、待ってください私がそれをするとき、私はこれを得ます:

間隔は適切ですが、画像とタイトルはビューのフレームの外にあります。

そうそう!私は忘れていました、ドキュメントはこれについて警告しました。彼らは部分的に言っています:

このプロパティは、レイアウト中に画像を配置するためにのみ使用されます。ボタンは、このプロパティを使用してintrinsicContentSizeおよびを決定しませんsizeThatFits:

しかし、助けることができるプロパティがあり、それはcontentEdgeInsetsです。そのためのドキュメントは、部分的に言っています:

ボタンは、このプロパティを使用してintrinsicContentSizeおよびを決定しsizeThatFits:ます。

いいですね。したがって、もう一度カテゴリを微調整しましょう。

@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で作業していて、何も考えたくないですか?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)
    }
}

25
なんて素晴らしい答えでしょう!パーティーには数年遅れましたがintrinsicContentSize、これは間違っているという問題を解決しました。これは、元の回答が受け入れられたため、これらの自動レイアウトの日に非常に重要です。
Acey、

2
そして、あなたは、ボタンの外側と画像とラベルの間の間隔と同じ量を望んでいたならば、追加しspacingそうのように、self.contentEdgeInsetsの4つの値のそれぞれに:self.contentEdgeInsets = UIEdgeInsetsMake(spacing, spacing + insetAmount, spacing, spacing + insetAmount);
NEUTデア・エリック・バン

1
正解です。残念ながら、画像を右揃えにするとうまく機能せず、テキストの長さが変わる可能性があります。
jlpiedrahita

2
ほぼ完璧な答えです!不足しているのは、Right-to-Leftインターフェイスで実行する場合、画像とタイトルのインセットを反転する必要があることです。
jeeeyul 2016年

画像の比率を1対1に設定する方法
Yestay Muratov

39

インターフェースビルダー。UIButton-> Attributes Inspector-> Edge = Titleを選択し、エッジインセットを変更します


38

また、次のようなものを作成したい場合

ここに画像の説明を入力してください

あなたは必要です

1.ボタンの水平方向と垂直方向の配置を設定します

ここに画像の説明を入力してください

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

これにより、ボタンにタイトルと画像が配置されます。

また、各再レイアウトでこれを更新してください。


迅速

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

29

これを使用することで多くのトラブルを回避できます-

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

これにより、すべてのコンテンツが自動的に左(または任意の場所)に配置されます

スウィフト3:

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

myButton.contentVerticalAlignment = UIControlContentVerticalAlignment.center; //
タイプミスを

25

ではXcodeの8.0あなたは、単に変更することによって、それを行うことができますinsetsサイズインスペクタで。

UIButton->属性インスペクター->サイズインスペクターに移動し、コンテンツ、画像、タイトルインセットを変更します。

ここに画像の説明を入力してください

また、右側の画像を変更する場合はForce Right-to-left、属性インスペクターでセマンティックプロパティをに変更するだけです。

ここに画像の説明を入力してください


私の場合、ボタンの画像はxcode 10でテキストの右側に移動しませんか?手伝ってくれますか?
Satish Mavani

こんにちはサティッシュ、これもxcode 10で正常に動作しています。背景画像ではなく画像を設定していることを願っており、サイズインスペクタで画像の昆虫を変更することもできます。
Sahil

18

私もこのパーティーに少し遅れますが、:o)を追加するのに役立つ情報があると思います。

UIButtonボタンの画像をどこに配置するかを垂直または水平に選択できるようにすることを目的としたサブクラスを作成しました。

それはあなたがこの種のボタンを作ることができることを意味します: 異なる種類のボタン

ここに私のクラスでこれらのボタンを作成する方法の詳細:

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タイトルと画像の間隔を調整できるという名前の属性も追加しました。

このクラスはimageEdgeInsets、を使用する場合、または新しいレイアウト属性titleEdgeInsetscontentEdgeInsets直接または組み合わせて使用​​する場合に互換性を持つように最善を尽くします。

@ravronが説明するように、ボタンのコンテンツの端が正しくなるように最善を尽くします(赤い枠でわかるように)。

Interface Builderでも使用できます。

  1. UIButtonを作成する
  2. ボタンクラスを変更する
  3. 「中央」、「上」、「下」、「左」または「右」を使用してレイアウト可能な属性を調整します ボタンの属性

ここにコード(要旨):

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

1
私はIBを壊さないために、いくつかのものを修正しerror: IB Designables: Failed to update auto layout status: The agent crashedgist.github.com/nebiros/ecf69ff9cb90568edde071386c6c4ddb
nebiros

@nebirosは何が間違っているのか、どのように修正するのか説明できますか?
gbitaudeau 2017

@gbitaudeauスクリプトをコピーして貼り付けると、エラーが発生しました。オーバーライドされなかったerror: IB Designables: Failed to update auto layout status: The agent crashedためです。init(frame: CGRect)また、@available注釈を追加しています… diff -Naur必要に応じて、;-)
nebiros

9

アカウントのロケール変更に対する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)
    }
}

6

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

使用法

button.centerTextAndImage(spacing: 10.0)

カスタム画像サイズでどのように使用できますか?
midhun p '20

2

私は以下のコードを書きます。製品バージョンでうまく動作します。Supprot Swift 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()
}
}

1

これは、imageEdgeInsetsの使用方法の簡単な例です。これにより、ヒット可能な領域が30x30のボタンに10ピクセル大きくなります(50x50)

    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)

0

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

-1

迅速な4.2バージョンのソリューションは次のようになります。

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)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.