Autolayout-UIButtonの固有サイズにはタイトルインセットが含まれません


196

自動レイアウトを使用してUIButtonを配置している場合、そのサイズはコンテンツに合わせて適切に調整されます。

画像を次のように設定した場合 button.image、本来のサイズがこれを考慮しているようです。

しかし、私が微調整した場合 titleEdgeInsetsボタンのと、レイアウトはこれを考慮せず、ボタンのタイトルを切り捨てます。

ボタンの固有の幅がインセットを占めるようにするにはどうすればよいですか?

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

編集:

私は以下を使用しています:

[self.backButton setTitleEdgeInsets:UIEdgeInsetsMake(0, 5, 0, 0)];

目標は、画像とテキストの間にいくらかの分離を加えることです。


3
これをレーダーとして提出しましたか?確かに、UIButtonの本質的なサイズ計算のバグのようです。
Ryan Poolos 2013

1
私はレーダーを提出する準備ができていましたが、これは実際には予期された動作のようです。これは、UIButtonの* EdgeInsetsプロパティに記載されています。「指定したインセットは、ボタンのテキストに合わせてサイズ変更されたタイトル長方形に適用されます。したがって、正のインセット値は実際にタイトルテキストをクリップする場合があります。[...]ボタンは、このプロパティを使用して、intrinsicContentSizeおよびsizeThatFits:を決定しません。」
ギヨームアルギス2014年

7
@GuillaumeAlgisこれは動作を述べたものですが、autolayoutを使用するときに予期されることはまったくないことを私は主張します。私はバグを報告したので、他の人にもバグを報告するように勧めます。
memmons 2014年

ここでレーダーバグにリンクできる場合、それをクリックして+1できますか?
gprasant

1
titleEdgeInsetドキュメントから:The insets you specify are applied to the title rectangle after that rectangle has been sized to fit the button’s text. Thus, positive inset values may actually clip the title text. インセットを追加することで、ボタンにテキストを確実にクリップさせます
Marco Pappalardo

回答:


192

これを解決するには、メソッドをオーバーライドしたり、任意の幅制約を設定したりする必要はありません。次のように、Interface Builderですべて実行できます。

  • 固有のボタンの幅は、タイトルの幅とアイコンの幅に加えて、左右のコンテンツエッジインセットから算出されます。

  • ボタンに画像とテキストの両方がある場合、それらはグループとして中央揃えされ、間にパディングはありません。

  • 左のコンテンツインセットを追加すると、テキスト+アイコンではなく、テキストを基準に計算されます。

  • 負の左画像インセットを設定すると、画像は左に引き出されますが、ボタン全体の幅は影響を受けません。

  • 負の左画像インセットを設定すると、実際のレイアウトはその値の半分を使用します。したがって、-20ポイントの左インセットを取得するには、Interface Builderで-40ポイントの左インセット値を使用する必要があります。

したがって、必要な左インセットアイコンとテキストの間の内部パディングの両方のためのスペースを作成するのに十分な大きさの左コンテンツインセットを提供し、アイコンとテキストの間に必要なパディングの量を2倍にして、アイコンを左にシフトします。その結果、左と右のコンテンツインセットが等しいボタンと、テキストとアイコンのペアがグループとして中央に配置され、その間に特定のパディングが挿入されます。

いくつかの例の値:

// Produces a button with the layout:
// |-20-icon-10-text-20-|
// AutoLayout intrinsic width works as you'd desire.
button.contentEdgeInsets = UIEdgeInsetsMake(10, 30, 10, 20)
button.imageEdgeInsets = UIEdgeInsetsMake(0, -20, 0, 0)

なぜ実際のレイアウトは負の左インセット値の半分を使用するのですか?同じ問題が発生しました!
Tony Lin、

1
回避策があることは素晴らしいことですが、これがの奇妙な動作を正当化するために使用されないことを願っていUIButtonます。
funct7 2018年

205

ネガティブとポジティブのタイトルとコンテンツインセットの組み合わせを使用することで、これをInterface Builderで(コードを記述せずに)機能させることができます。

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

更新:Xcode 7には、[ Rightインセット]フィールドに負の値を入力できないというバグがありますが、横にあるステッパーコントロールを使用して値を減らすことができます。(ありがとうスチュアート)

これを行うと、画像とタイトルの間に8ptのスペースが追加され、ボタンの固有の幅が同じ量だけ増加します。このような:

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


2
contentEdgeInsets(バグではない)を使用して、自動レイアウトでボタンの幅を広げています。ラベルを右側の空きスペースに移動します。タイトルエッジインセットバグを回避するための賢い方法。
ugur

7
このトリックはもはや機能しません。インターフェイスビルダーは、Rightフィールドに負の値を受け入れなくなりました。
Joris Mans

7
@JorisMans 負の値を入力することはできませんが、テキストフィールドの右側にあるステッパーコントロールを使用して必要な負の値にステップダウンすることで、うまくいきました。
スチュアート

3
これが最初の答えになるはずですが、なぜここにあるのですか?私はこれを見つける前に他の5つを試しました...
Zsolt卿

2
UIButtonのテキストを中央に配置するためにコンテンツの権利インセット16を作成しました
coolcool1994 '28

96

intrinsicContentSizeUIViewでメソッドをオーバーライドしないのはなぜですか?例えば:

- (CGSize) intrinsicContentSize
{
    CGSize s = [super intrinsicContentSize];

    return CGSizeMake(s.width + self.titleEdgeInsets.left + self.titleEdgeInsets.right,
                      s.height + self.titleEdgeInsets.top + self.titleEdgeInsets.bottom);
}

これにより、自動レイアウトシステムにボタンのサイズを大きくしてインセットを許可し、テキスト全体を表示するように指示します。私は自分のコンピューターではないので、これをテストしていません。


1
私の知る限り、ボタンはオーバーライドすべきではありません。問題は、すべてのボタンタイプが異なるサブクラスによって実装されていることです。
スルタン2013

2
intrinsicContentSizeUIButtonではなく、UIViewのメソッドなので、UIButtonメソッドをいじる必要はありません。Appleはそれを問題だとは考えていません。「このメソッドをオーバーライドすると、カスタムビューがコンテンツに基づいてどのサイズにしたいかをレイアウトシステムに伝えることができます。」そして、OPはさまざまなボタンについて何も言わず、1つだけを言いました。
Maarten 2013

1
これは間違いなく機能し、私が行ったソリューションです。 intrinsicContentSize確かにUIViewのメソッドであり、UIButtonはUIViewのサブクラスであるため、もちろんこのメソッドをオーバーライドできます。Appleのドキュメントには、すべきでないと書かれているものはありません。Maartenのオーバーライドされたメソッドを使用してUIButtonサブクラスを作成し、Interface BuilderのUIButtonをYourUIButtonSubclassタイプに変更すれば、完全に機能します。
n8tr 2014年

37
私には思えるintrinsicContentSizeUIButtonがtitleEdgeInsetsに追加する必要のために、私はアップルにバグを報告するつもりです。
progrmr 14

6
私も同意し、imageEdgeInsetsについても同じです。
Ricardo Sanchez-Saez 2014

87

インセットの設定方法を指定していないため、titleEdgeInsetsを使用していると思います。これは、同じ効果が得られるためです。代わりにcontentEdgeInsetsを使用すると、正しく動作します。

- (IBAction)ChangeTitle:(UIButton *)sender {
    self.button.contentEdgeInsets = UIEdgeInsetsMake(0,20,0,20);
    [self.button setTitle:@"Long Long Title" forState:UIControlStateNormal];
}

実際、titleEdgeInsetsを使用しています。タイトルを画像から離す必要があります。ボタンの端から画像を離す必要はありません。多分私はそれにいくつかのパディングを含む画像を使用する必要がありますか?ハックのようですが。
ベンパッカード

これは、autolayoutと組み合わせて完全に機能します。ありがとうございます。
Cal S

3
これは、intelligentContentSizeを変更せずに必要な機能を実行できるため、より優れたソリューションです。
RyJ 2014年

28
これは、画像を使用していて、画像とタイトルの間のインセットを調整する必要がある場合の質問には答えません!
Brody Robertson、

23

そしてSwiftにとってこれは働いた:

extension UIButton {
    override open var intrinsicContentSize: CGSize {
        let intrinsicContentSize = super.intrinsicContentSize

        let adjustedWidth = intrinsicContentSize.width + titleEdgeInsets.left + titleEdgeInsets.right
        let adjustedHeight = intrinsicContentSize.height + titleEdgeInsets.top + titleEdgeInsets.bottom

        return CGSize(width: adjustedWidth, height: adjustedHeight)
    }
}

Love U Swift


1
想定されていない場合でも、この場合はサブクラス化することをお勧めします。Appleのドキュメントでは、組み込みサイズには計算にtitleEdgeInsetsが含まれていないことが明示されているため、拡張機能を使用すると、Appleの期待だけでなく、他のすべての開発者が違反することになります。ドキュメント。
サイレン2019年

18

このスレッドは少し古いですが、私は自分でこれに遭遇し、ネガティブインセットを使用して解決できました。たとえば、ここで目的のパディング値に置き換えます。

UIButton* myButton = [[UIButton alloc] init];
// setup some autolayout constraints here
myButton.titleEdgeInsets = UIEdgeInsetsMake(-desiredBottomPadding,
                                            -desiredRightPadding,
                                            -desiredTopPadding,
                                            -desiredLeftPadding);

適切な自動レイアウト制約と組み合わせると、画像とテキストを含む自動サイズ変更ボタンが作成されます。desiredLeftPadding10に設定して以下を参照してください。

画像と短いテキストのボタン

画像と長いテキストのボタン

ボタンの実際のフレームはラベルを囲んでいないことがわかります(ラベルは境界の外側で10ポイント右にシフトされているため)が、テキストと画像の間に10ポイントのパディングが達成されています。


1
これは、サブクラス化を必要としないため、私が使用したソリューションです。仕事あなたのボタンがバックグラウンドを持っているが、それは通常のiOS 7との問題ではない場合はありません
ホセ・マヌエル・サンチェス

これは、ボタンのコンテンツオフセット(正の値> =タイトルインセット)も設定した場合、背景画像で機能します。
ベンフリン

9

pegpegの回答に基づくSwift 3の場合:

extension UIButton {

    override open var intrinsicContentSize: CGSize {

        let intrinsicContentSize = super.intrinsicContentSize

        let adjustedWidth = intrinsicContentSize.width + titleEdgeInsets.left + titleEdgeInsets.right
        let adjustedHeight = intrinsicContentSize.height + titleEdgeInsets.top + titleEdgeInsets.bottom

        return CGSize(width: adjustedWidth, height: adjustedHeight)

    }

}

こんにちは。インターフェイスビルダーでカスタム拡張ボタンを使用したい。
plz

6

上記のすべてがiOS 9以降では機能しませんでした。

  • 幅の制約を追加します(ボタンにテキストがない場合の最小幅用。テキストが指定されている場合、ボタンは自動スケーリングされます)
  • 関係をより大きいか等しいに設定する

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

ボタンの周囲にボーダーを追加するには、次のメソッドを使用します。

button.contentEdgeInsets = UIEdgeInsetsMake(0,20,0,20);

何故なの?内容に合わせて自動的に
拡大縮小し

最小幅を定義したからです。自動レイアウトの全体的なアイデアは、明示的な(最小)幅を設定せずにそれを行うことです。
Joris Mans

幅についてではなく、必要に応じて幅を1に設定できますが、自動レイアウトでは、幅が同じかそれ以上になることを知っている必要があります。私は回答を更新しました
Oritm

幅の制約はまったく必要ありません。contentEdgeInsetが重要です。自動レイアウトはそれを固有のコンテンツサイズに使用します。
Chris Conover、2018年

5

UIButtonアイコンとラベルの間に5ポイントのスペースを追加したいと思いました。これは私がそれを達成した方法です:

UIButton *infoButton = [UIButton buttonWithType:UIButtonTypeCustom];
// more button config etc
infoButton.contentEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 5);
infoButton.titleEdgeInsets = UIEdgeInsetsMake(0, 5, 0, -5);

contentEdgeInsets、titleEdgeInsets、およびimageEdgeInsetsを相互に関連付ける方法には、各インセットから少しのギブアンドテイクが必要です。したがって、タイトルの左側にインセットを追加する場合は、右側に負のインセットを追加し、コンテンツの右側に(正のインセットを介して)スペースを追加する必要があります。

タイトルインセットのシフトに合わせて適切なコンテンツインセットを追加することで、テキストがボタンの境界の外に出ることはありません。


3

このオプションは、インターフェイスビルダーでも使用できます。挿入図を参照してください。左右を3に設定しました。

インターフェイスビルダーのスクリーンショット


1
ええ、この答えが説明するように、それが機能する理由は、Edge:TitleまたはEdge:Imageではなく、ここでEdge:Contentを調整しているためです。
スマイリーボー2015年

1

私が使用する解決策は、ボタンに幅の制約を追加することです。次に、初期化のどこかで、テキストを設定した後、幅の制約を次のように更新します。

self.buttonWidthConstraint.constant = self.shareButton.intrinsicContentSize.width + 8;

ここで、8はインセットが何であってもです。


buttonWidthConstraintとは何ですか?
アレクセイゴリコフ2014年


1
ボタンの固有のコンテンツサイズが変更された場合、constant制約のを新しい値に手動で更新する必要があるため、これは優れたソリューションではありません。ボタンをサブクラス化することなく。
スマイリーボー2015年

ああ。この方法はもう使用しません。それがダウン票の価値があるだったが、驚い_(ツ)_ /¯
ボブSpryn

への呼び出しsetNeedsUpdateConstraintsは、ボタンのタイトルまたは画像を更新した後に「手動で」行うことができます。その後、から定数をオーバーライドupdateConstraintsして再計算できますbuttonWidthConstraint。これは必ずしも最良のアプローチではありませんが、私にとっては十分に機能します。YMMV;)
Olivier

1

sizeToFit()を呼び出すと、contentEdgeInsetが有効になります。

extension UIButton {

   open override func draw(_ rect: CGRect) { 
       self.contentEdgeInsets = UIEdgeInsets(top: 10, left: 40, bottom: 10, right: 40)
       self.sizeToFit()
   }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.