自动布局 - UIButton 的固有大小不包括标题插图
Posted
技术标签:
【中文标题】自动布局 - UIButton 的固有大小不包括标题插图【英文标题】:Autolayout - intrinsic size of UIButton does not include title insets 【发布时间】:2013-07-21 22:31:17 【问题描述】:如果我有一个使用自动布局排列的 UIButton,它的大小会很好地调整以适应它的内容。
如果我将图像设置为button.image
,则内部尺寸似乎再次说明了这一点。
但是,如果我调整按钮的 titleEdgeInsets
,布局不会考虑这一点,而是截断按钮标题。
如何确保按钮的固有宽度占 inset 的比例?
编辑:
我正在使用以下内容:
[self.backButton setTitleEdgeInsets:UIEdgeInsetsMake(0, 5, 0, 0)];
目标是在图像和文本之间添加一些分隔。
【问题讨论】:
您是否将其归档为雷达?这显然是 UIButton 的固有尺寸计算中的一个错误。 我准备提交雷达,但这实际上似乎是预期的行为。这记录在UIButton's *EdgeInsets 属性中:“在该矩形的大小调整为适合按钮文本之后,您指定的插入将应用于标题矩形。因此,正插入值实际上可能会剪切标题文本。[...]按钮不使用此属性来确定intrinsicContentSize 和sizeThatFits:。" @GuillaumeAlgis 我会争辩说,虽然这是声明的行为,但在使用自动布局时,这根本不是人们所期望的。我已经提交了一个错误,并会鼓励其他人也提交一个。 如果你可以在这里链接到雷达错误,我们可以点击它并 +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.
因此,通过添加插图,您肯定会强制按钮剪辑文本
【参考方案1】:
您可以在 Interface Builder 中使用此功能(无需编写任何代码),方法是结合使用正负标题和内容插入。
更新:Xcode 7 有一个错误,您无法在 Right
Inset 字段中输入负值,但您可以使用它旁边的步进控件来减少价值。 (感谢斯图尔特)
这样做会在图像和标题之间增加 8pt 的间距,并将按钮的固有宽度增加相同的量。像这样:
【讨论】:
它使用 contentEdgeInsets(不是错误的)来让自动布局增加按钮宽度。并将标签移动到右侧的空白处。解决标题边缘插入错误的巧妙方法。 这个技巧不再有效。接口生成器不再接受Right
字段中的负值。
@JorisMans 您不能键入负值,但它对我有用,方法是使用文本字段右侧的步进器控件逐步降低到所需的负值。 ..去图!
这应该是第一个答案,为什么会在这里?在找到这个之前我已经尝试了其他 5 个......
我在内容插入 16 的右侧使 UIButton 中的文本居中【参考方案2】:
您无需重写任何方法或设置任意宽度约束即可解决此问题。您可以按如下方式在 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)
【讨论】:
为什么实际布局使用了一半的负左插入值??我也遇到了同样的问题! 很高兴有一个解决方法,但我希望这不是用来证明UIButton
的奇怪行为。【参考方案3】:
为什么不覆盖 UIView 上的intrinsicContentSize
方法?例如:
- (CGSize) intrinsicContentSize
CGSize s = [super intrinsicContentSize];
return CGSizeMake(s.width + self.titleEdgeInsets.left + self.titleEdgeInsets.right,
s.height + self.titleEdgeInsets.top + self.titleEdgeInsets.bottom);
这应该告诉自动布局系统它应该增加按钮的大小以允许插入并显示全文。我不在自己的电脑上,所以我没有测试过。
【讨论】:
据我所知,不应覆盖按钮。问题是每个按钮类型都是由不同的子类实现的。intrinsicContentSize
是 UIView 上的方法,而不是 UIButton,因此您不会弄乱任何 UIButton 方法。 Apple 认为这不是问题:“重写此方法允许自定义视图与布局系统通信,它希望基于其内容的大小。”并且 OP 没有说任何关于不同按钮的内容,只是一个。
这绝对有效,也是我采用的解决方案。 intrinsicContentSize
确实是 UIView 上的一个方法,而 UIButton 是 UIView 的一个子类,所以你当然可以重写这个方法; Apple 的文档中没有任何内容表明您不应该这样做。只需使用 Maarten 的重写方法创建一个 UIButton 子类,然后将 Interface Builder 中的 UIButton 更改为 YourUIButtonSubclass 类型,它就会完美地工作。
在我看来 intrinsicContentSize
的 UIButton 应该在 titleEdgeInsets 中添加,我将向 Apple 提交错误。
我同意,对于 imageEdgeInsets 也是如此。【参考方案4】:
您尚未指定如何设置插图,所以我猜您正在使用 titleEdgeInsets,因为我看到您获得的效果相同。如果我改用 contentEdgeInsets,它可以正常工作。
- (IBAction)ChangeTitle:(UIButton *)sender
self.button.contentEdgeInsets = UIEdgeInsetsMake(0,20,0,20);
[self.button setTitle:@"Long Long Title" forState:UIControlStateNormal];
【讨论】:
我确实在使用 titleEdgeInsets。我需要将标题与图像保持距离,而不是图像与按钮边缘的距离。也许我应该只使用带有一些填充的图像?不过看起来很老套。 这与自动布局完美结合,谢谢! 这是更好的解决方案,因为它完全符合您的要求,而无需触及intrinsicContentSize。 这并不能回答使用图像时需要调整图像和标题之间的插图的问题!【参考方案5】:对于 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)
爱你斯威夫特
【讨论】:
即使您不应该这样做,但在这种情况下最好进行子类化,因为 Apple 文档明确声明内在大小不包括 titleEdgeInsets 在其计算中,因此使用扩展您不仅违反Apple 的期望,但所有其他阅读文档的开发人员。 不支持在扩展内覆盖,并导致未定义的运行时行为。见:***.com/a/38274660/4175475【参考方案6】:这个线程有点老了,但我自己也遇到了这个问题,并且能够通过使用负插图来解决它。例如,在此处替换所需的填充值:
UIButton* myButton = [[UIButton alloc] init];
// setup some autolayout constraints here
myButton.titleEdgeInsets = UIEdgeInsetsMake(-desiredBottomPadding,
-desiredRightPadding,
-desiredTopPadding,
-desiredLeftPadding);
结合正确的自动布局约束,您最终会得到一个包含图像和文本的自动调整大小按钮!如下所示,desiredLeftPadding
设置为 10。
您可以看到按钮的实际框架不包含标签(因为标签向右移动了 10 个点,超出了边界),但我们在文本和文本之间实现了 10 个填充点图片。
【讨论】:
这是我使用的解决方案,因为它不需要子类化。如果您的按钮有背景,则不起作用,但这通常不是 ios 7 的问题 如果您还设置了按钮的内容偏移量(正值 >= 标题插入),这将与背景图像一起使用。【参考方案7】:对于 Swift 3 基于pegpeg 的回答:
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 中使用自定义扩展按钮。请帮助【参考方案8】:以上所有不适用于iOS 9+,我所做的是:
添加宽度约束(当按钮没有任何文本时的最小宽度。如果提供文本,按钮将自动缩放) 将关系设置为大于或等于现在要在按钮周围添加边框,只需使用以下方法:
button.contentEdgeInsets = UIEdgeInsetsMake(0,20,0,20);
【讨论】:
为什么不呢?它会根据内容自动缩放,您只需设置最小宽度(可以小于要显示的文本) 因为您定义了最小宽度。自动布局的整个想法是在不设置任何显式(最小)宽度的情况下完成。 不是关于宽度的,你可以根据自己的喜好设置宽度为1,但自动布局需要知道宽度可以相等或更多。我更新了我的答案 您根本不需要宽度约束,contentEdgeInset 是关键,然后自动布局将其用于固有内容大小。【参考方案9】:我想在我的 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 相互关联的方式需要对每个插图进行一些取舍。因此,如果您在标题左侧添加一些插图,则必须在右侧添加负插图,并在内容右侧提供更多空间(通过正插图)。
通过添加正确的内容插图以匹配标题插图的移动,我的文本不会超出按钮的范围。
【讨论】:
【参考方案10】:该选项在界面生成器中也可用。见插图。我将左右设置为 3。就像一个魅力。
【讨论】:
是的,正如this answer 解释的那样,它起作用的原因是您在这里调整 Edge: Content 而不是 Edge: Title 或 边缘:图像.【参考方案11】:我使用的解决方案是在按钮上添加宽度约束。然后在初始化的某个地方,设置好文本后,像这样更新宽度约束:
self.buttonWidthConstraint.constant = self.shareButton.intrinsicContentSize.width + 8;
其中 8 是您的插图。
【讨论】:
什么是buttonWidthConstraint? @AlexeyGolikov An NSLayoutConstraint -- developer.apple.com/library/mac/documentation/AppKit/Reference/… 这不是一个很好的解决方案,因为如果按钮的内在内容大小发生变化,您需要手动将约束的constant
更新为新值......并且知道当按钮的内在内容大小发生变化时,如果不对按钮进行子类化是很困难的。
哎呀。我不再使用这种方法了。很惊讶它值得投反对票,但是¯_(ツ)_/¯
可以在更新按钮标题或图像后“手动”调用setNeedsUpdateConstraints
。然后您可以覆盖updateConstraints
并从那里重新计算buttonWidthConstraint
的常量。这不一定是最好的方法,但它对我来说已经足够好了。 YMMV ;)以上是关于自动布局 - UIButton 的固有大小不包括标题插图的主要内容,如果未能解决你的问题,请参考以下文章
Interface Builder 运行时自动布局约束阻止视图占用固有大小,我该如何解决这个问题?
stackView swift中uibutton问题的自动布局/宽度