ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

但是,如果我调整按钮的 titleEdgeInsets,布局不会考虑这一点,而是会截断按钮标题。

如何确保按钮的固有宽度占插图?

https://i.stack.imgur.com/bFikp.png

编辑:

我正在使用以下内容:

[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. 因此,通过添加插图,您将强制按钮确定剪辑文本

n
n.Drake

您可以通过结合使用否定和肯定的标题和内容插入,在 Interface Builder 中使其工作(无需编写任何代码)。

https://i.stack.imgur.com/wnBnd.png

更新:Xcode 7 有一个错误,您无法在 Right Inset 字段中输入负值,但您可以使用它旁边的步进控件来减少价值。 (谢谢斯图尔特)

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

https://i.stack.imgur.com/g5azb.png


它使用 contentEdgeInsets (不是错误的)来让自动布局增加按钮宽度。并将标签移动到右侧的空白处。解决标题边缘插入错误的巧妙方法。
这个技巧不再有效。界面生成器不再接受 Right 字段中的负值。
@JorisMans您不能输入负值,但它对我有用,方法是使用文本字段右侧的步进器控件逐步降低到所需的负值......去图!
这应该是第一个答案,为什么会在这里?在找到这个之前我已经尝试了其他5个......
我将内容插入右侧 16 以使 UIButton 中的文本居中
j
jaredsinclair

您无需重写任何方法或设置任意宽度约束即可解决此问题。您可以在 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 的奇怪行为的合理性。
i
itsji10dra

为什么不覆盖 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 也是如此。
r
rdelmar

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

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

我确实在使用titleEdgeInsets。我需要将标题与图像保持距离,而不是图像与按钮边缘的距离。也许我应该只使用带有一些填充的图像?虽然看起来很hacky。
这与自动布局完美结合,谢谢!
这是更好的解决方案,因为它完全符合您的要求,而无需触及intrinsicContentSize。
当使用图像并且需要调整图像和标题之间的插图时,这并不能回答问题!
m
monkee

而对于 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 的期望,而且违反了所有其他阅读过的开发人员文档。
不支持在扩展内覆盖并导致未定义的运行时行为。见:stackoverflow.com/a/38274660/4175475
s
shim

这个线程有点老了,但我自己也遇到了这个问题,并且能够通过使用负插图来解决它。例如,在这里替换您想要的填充值:

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

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

https://i.stack.imgur.com/RVdm9.png

https://i.stack.imgur.com/s6lL8.png

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


这是我使用的解决方案,因为它不需要子类化。如果您的按钮有背景将不起作用,但这通常不是 iOS 7 的问题
如果您还设置了按钮的内容偏移量(正值 >= 标题插图),这将适用于背景图像。
o
orj

我想在我的 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 相互关联的方式需要从每个插图中进行一些取舍。因此,如果您在标题的左侧添加一些插图,则必须在右侧添加负插图并在内容右侧提供更多空间(通过正插图)。

通过添加正确的内容插图以匹配标题插图的移动,我的文本不会超出按钮的范围。


T
TheoK

对于基于 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 中使用自定义扩展按钮。请帮助
O
Oritm

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

添加宽度约束(当按钮没有任何文本时为最小宽度。如果提供文本,按钮将自动缩放)

将关系设置为大于或等于

https://i.stack.imgur.com/Zafor.png

现在要在按钮周围添加边框,只需使用以下方法:

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

为什么不?它会自动随内容缩放,您只需设置最小宽度(可以小于要显示的文本)
因为您定义了最小宽度。自动布局的整个想法是在不设置任何显式(最小)宽度的情况下完成。
它与宽度无关,如果您愿意,可以将宽度设置为 1,但自动布局需要知道宽度可以相等或更大。我更新了我的答案
您根本不需要宽度约束, contentEdgeInset 是关键,然后自动布局将其用于内在内容大小。
B
Ben Packard

该选项在界面生成器中也可用。见插图。我将左右设置为 3。就像一个魅力。

https://i.stack.imgur.com/Nkmzv.png


是的,正如 this answer 所解释的,它起作用的原因是因为您在此处调整 Edge: Content 而不是 Edge: TitleEdge: Image
B
Bob Spryn

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

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

无论您的插图是什么,8 都在哪里。


什么是按钮宽度约束?
@AlexeyGolikov 一个 NSLayoutConstraint -- developer.apple.com/library/mac/documentation/AppKit/Reference/…
这不是一个很好的解决方案,因为如果按钮的内在内容大小发生变化,您需要手动将约束的 constant 更新为新值......并知道按钮的内在内容大小何时如果没有子类化按钮,更改是困难的。
哎呀。我不再使用这种方法了。很惊讶它值得投反对票但是¯_(ツ)_/¯
更新按钮标题或图像后,可以“手动”调用 setNeedsUpdateConstraints。然后您可以覆盖 updateConstraints 并从那里重新计算 buttonWidthConstraint 的常数。这不一定是最好的方法,但它对我来说已经足够好了。 YMMV ;)

关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅