自动布局-UIButton的固有大小不包括标题插图


196

如果我有一个使用自动布局排列的UIButton,它的大小会很好地调整以适合其内容。

如果我将图像设置为button.image,则内在大小似乎再次说明了这一点。

但是,如果我调整titleEdgeInsets按钮的,则布局不会解决此问题,而是会截断按钮标题。

如何确保按钮的固有宽度能说明插图?

在此处输入图片说明

编辑:

我正在使用以下内容:

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

目的是在图像和文本之间添加一些分隔。


3
您是否将其归档为雷达?当然,这似乎是UIButton固有尺寸计算中的错误。
Ryan Poolos 2013年

1
我已经准备好提起雷达,但这实际上似乎是预期的行为。这在UIButton的* EdgeInsets属性中有记录:“在将矩形调整大小以适合按钮的文本之后,将指定的插入应用于标题矩形。因此,正的插入值实际上可能会剪切标题文本。[...]按钮不使用此属性来确定internalContentSize和sizeThatFits:。”
纪尧姆·阿尔吉斯

7
@GuillaumeAlgis我认为,虽然这是规定的行为,它是不是在所有什么人会想到使用自动布局时发生。我提出了一个错误,并鼓励其他人也提出一个错误。
备忘录

如果您可以在此处链接到雷达臭虫,我们可以单击它并对其进行+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

Answers:


192

您可以解决此问题,而不必覆盖任何方法或设置任意宽度约束。您可以按照以下步骤在Interface Builder中完成所有操作。

  • 本质按钮的宽度是从标题宽度加上图标宽度加上左右内容边缘插图得出的。

  • 如果按钮同时具有图像和文本,则它们将作为一个组居中,并且之间没有填充。

  • 如果添加左内容插图,则它是相对于文本而不是文本+图标计算的。

  • 如果将负图像插入设置为负,则图像会向左拉出,但按钮的整体宽度不会受到影响。

  • 如果将负图像插入设置为负,则实际布局将使用该值的一半。因此,要获得-20点的左插入,必须在Interface Builder中使用一个-40点的左插入值。

因此,您需要提供足够大的左侧内容插图,以为所需的左侧插图创建空间 图标与文本之间的内部填充,然后通过将图标和文本之间所需的填充量加倍来向左移动图标。结果是一个具有相等的左右内容插入量的按钮,以及一个以组为中心的文本和图标对,它们之间具有特定的填充量。

一些示例值:

// 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

205

通过组合使用负的和正的标题和内容插图,可以使它在Interface Builder中工作(无需编写任何代码)。

在此处输入图片说明

更新:Xcode 7有一个错误,您不能在“ Right插入”字段中输入负值,但是可以使用它旁边的步进控件来减小该值。(感谢Stuart)

这样做将在图像和标题之间增加8pt的间距,并将按钮的固有宽度增加相同的数量。像这样:

在此处输入图片说明


2
它使用contentEdgeInsets(不是越野车)来使自动布局增加按钮的宽度。并将标签移到右侧的空白处。解决标题边缘插入错误的巧妙方法。
ugur

7
此技巧不再起作用。界面构建器不再在该Right字段中接受负值。
Joris Mans 2015年

7
@JorisMans您不能输入负值,但是它对我有用,方法是使用文本字段右侧的步进控件降低到所需的负值。
斯图尔特

3
这应该是第一个答案,为什么在这里呢?在找到这个之前,我已经尝试了其他5个...
佐索尔勋爵(Lord Zsolt)

2
我将“内容插入右16”设置为UIButton中的文本居中
coolcool1994

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
据我所知,不应覆盖按钮。问题在于每种按钮类型都由不同的子类实现。
苏珊(Sulthan)2013年

2
intrinsicContentSize是UIView上的方法,而不是UIButton上的方法,因此您不会被任何UIButton方法弄乱。苹果公司认为这不是问题:“重写此方法可以使自定义视图与布局系统进行通信,基于其内容,其尺寸是多少。” OP并没有对不同的按钮说任何话,只是一个。
Maarten

1
这绝对有效,并且是我所采用的解决方案。 intrinsicContentSize确实是UIView上的方法,而UIButton是UIView的子类,因此您当然可以覆盖此方法;苹果文档中没有任何内容表明您不应该这样做。只需使用Maarten的重写方法创建UIButton子类,然后在Interface Builder中将UIButton更改为YourUIButtonSubclass类型,即可正常使用。
n8tr 2014年

37
在我看来intrinsicContentSize,UIButton应该添加在titleEdgeInsets中,我将向Apple提交错误。
progrmr 2014年

6
我同意,对于imageEdgeInsets也是如此。
里卡多·桑切斯·塞兹

87

您尚未指定如何设置插图,所以我猜您正在使用titleEdgeInsets,因为我看到的效果与以前相同。如果我使用contentEdgeInsets,它将正常工作。

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

我确实在使用titleEdgeInsets。我需要使标题与图像保持距离,而不是图像与按钮边缘的距离。也许我应该只使用带有填充的图像?虽然似乎很hacky。
Ben Packard

这与自动布局完美结合,谢谢!
Cal S

3
这是更好的解决方案,因为它完全可以实现您想要的功能,而无需触及internalContentSize。
RyJ

28
使用图像并需要调整图像和标题之间的插图时,这不能回答问题!
布罗迪·罗伯逊

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

爱你迅捷


1
即使您不应该这样做,也最好在这种情况下进行子类化,因为Apple文档明确声明内在大小在计算中不包括titleEdgeInsets,因此使用扩展名不仅违反了Apple的期望,而且还违反了所有其他阅读内容的开发人员文档。
塞伦斯(Sirens)

18

这个线程有点旧,但是我自己碰到了这个问题,并且可以通过使用负数插入来解决它。例如,在此处替换所需的填充值:

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

结合正确的自动布局约束,您最终将获得一个自动调整大小的按钮,其中包含图像和文本!如下所示,desiredLeftPadding设置为10。

带有图像和短文本的按钮

带有图像和长文本的按钮

您可以看到按钮的实际框架不包含标签(因为标签在边界之外向右移动了10点),但是我们在文本和图片之间实现了10点的填充。


1
这是我使用的解决方案,因为它不需要子类化。将无法正常工作,如果你的按钮有一个背景,但这并不是通常与iOS 7的一个问题
若泽·曼努埃尔·桑切斯

如果您还设置按钮的内容偏移量(正值> =标题插入),则它将与背景图像一起使用。
Ben Flynn 2014年

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)

    }

}

你好。我想在interfacebuilder中使用自定义扩展按钮。请帮助
kemdo '18

6

以上所有内容均不适用于iOS 9+,我所做的是:

  • 添加宽度限制(当按钮没有任何文本时为最小宽度。如果提供了文本,则按钮将自动缩放)
  • 将关系设置为大于或等于

在此处输入图片说明

现在只需在方法周围添加边框即可:

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

为什么不?它会随内容自动缩放,您只需要设置最小宽度(可以小于要显示的文本的宽度)即可
Oritm 2015年

因为您定义了最小宽度。无需设置任何显式(最小)宽度即可完成自动布局的整个想法。
Joris Mans 2015年

它与宽度无关,您可以根据需要将宽度设置为1,但是自动布局需要知道宽度可以相等或更大。我更新了答案
Oritm

您根本不需要宽度限制,contentEdgeInset是关键,然后自动布局将其用于内部内容大小。
克里斯·康诺夫

5

我想在UIButton图标和标签之间添加5pt的空间。这是我实现的方式:

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:Content而不是Edge:TitleEdge:Image
smileyborg

1

我使用的解决方案是在按钮上添加宽度约束。然后在初始化的某个地方,设置完文本后,像下面这样更新宽度约束:

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

8是您的插图。


什么是buttonWidthConstraint?
Alexey Golikov 2014年


1
这不是一个很好的解决方案,因为如果按钮的内在内容大小发生变化,则需要手动constant将约束条件的值更新为新值...并且很难知道按钮的内在内容大小何时会发生变化而不将按钮子类化。
smileyborg

阿y 我不再使用这种方法。感到惊讶的是,它值得被否决,但__(ツ)_ //
Bob Spryn 2015年

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()
   }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.