【问题标题】:IOS: Adjust UIButton height depend on title text using Autolayout?IOS:调整 UIButton 高度取决于使用 Autolayout 的标题文本?
【发布时间】:2016-04-07 21:57:38
【问题描述】:

我有一个UIButton,它可以在运行时更改标题。因此,我想增加UIButton 的高度取决于标题文本,以使用AutoLayout 显示全文。

我可以通过将height constraint 设置为“大于或等于”来增加UILabel 的高度,但它不适用于UIButton
我使用了[myButton sizeToFit],但它只会增加UIButon 的宽度(不会增加高度)。

我目前的UIButton 属性现在是
- 约束高度:30 - 领先:15 - 尾随:15 - 前5 - 字体大小:12

更新
我为UIButton 的约束高度创建了一个IBOutlet,用于更改@NSNood 所说的高度。
然后我需要在标题文本中使用\n 来分割行。
但我不知道应该把\n 放在哪里?

这是我想要的纵向Button

这是我想要横向显示的Button

如何确定放置\n的位置?

请指导我如何使用AutoLayout 实现它。任何帮助,将不胜感激。

【问题讨论】:

  • 创建按钮高度约束的IBOutlet,并在需要时将其更改为常量。例如。如果你想设置高度等于 30,你可以这样做:btnHeightConstraint.constant = 30.
  • 当我只增加按钮的约束高度时,文本不会分成2行,它仍然是1行
  • 如果标题能够适应按钮的给定宽度,它不会分成两行。如果你想设置多行标题,你可以使用\n将属性字符串设置为标题。
  • 但是如果我在更大的设备上运行应用程序,或者我旋转屏幕,按钮文本会显示错误
  • 如果你应用了适当的约束,那么它在更大的设备上看起来还不错。 Idk 关于轮换方案。

标签: ios objective-c iphone uibutton autolayout


【解决方案1】:

关键是如果你设置了sizeToFit属性,那么文本会一直在一行并且按钮的宽度会增加,除非你放一个下一行符号\n来明确的说你想要它是几行。

你把'\n'放在第一行的末尾,比如"line \n line",它代表

line
line

如果您想为 PortraitLandscape 设置两个不同的字符串值(\n 的位置不同),您可以使用 UIDeviceOrientation (@ 987654328@) described here 并根据设备的方向设置一个字符串值

【讨论】:

  • sizeToFit 甚至不适用于自动布局
  • @sizeToFit 我解释了如何正确使用sizeToFit,因为他最初尝试使用它
【解决方案2】:

抱歉,我最近没有关注该帖子,因此我想出了一个真正迟到的解决方案。我仍然在写答案作为参考,如果将来有人会发现它有用的话。

首先让我们展示按钮的情节提要配置。如下图所示:

图片显示我只为按钮添加了左、上和右约束,没有其他任何东西。这允许按钮有一些intrinsicContentSize 的高度,但它的宽度仍然由它的左右约束决定。

下一个阶段是编写一些包含按钮的 ViewController 类。在我的 VC 中,我为按钮创建了一个出口,名称为 button

@property(nonatomic,weak) IBOutlet UIButton* button;

并将其附加到情节提要按钮上。现在我已经覆盖了两种方法,即viewDidLoadviewWillLayoutSubviews,如下所示:

-(void)viewDidLoad {
    [super viewDidLoad];

    self.button.titleLabel.numberOfLines = 0;
    self.button.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
}
-(void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];

    [self.button setTitle:@"Chapter One\n "
     "A Stop on the Salt Route\n "
     "1000 B.C.\n "
     "As they rounded a bend in the path that ran beside the river, Lara recognized the silhouette of a fig tree atop a nearby hill. The weather was hot and the days were long. The fig tree was in full leaf, but not yet bearing fruit." forState:UIControlStateNormal];
}
  1. viewDidLoad 方法确保titleLabel( 保存按钮文本)是多行的,如果出现一些大文本, 它通过换行来换行文本。
  2. viewWillLayoutSubviews 方法确保按钮布局过程 每当主视图的边界发生变化时发生,例如因为 改变界面方向。

最后也是最有效的部分是手动处理按钮的布局过程。为此,我们需要继承UIButton。我编写了一个名为MyButton 的子类,它继承自UIButton,你可以使用任何你喜欢的名字。将此设置为 Identity Inspector 中按钮的自定义类。

子类覆盖两个方法,即intrinsicContentSizelayoutSubviews。类主体如下所示:

#import "MyButton.h"

@implementation MyButton

-(CGSize)intrinsicContentSize {
    return [self.titleLabel sizeThatFits:CGSizeMake(self.titleLabel.preferredMaxLayoutWidth, CGFLOAT_MAX)];;
}
-(void)layoutSubviews {
    self.titleLabel.preferredMaxLayoutWidth = self.frame.size.width;
    [super layoutSubviews];
}
@end

UIButon 子类通过覆盖layoutSubviews 方法来获得布局过程的所有权。这里的基本思想是确定按钮宽度,一旦它已经布局。然后将其子titleLabel(保存按钮文本的标签)的宽度设置为preferredMaxLayoutWidth(布局引擎的最大宽度,多行标签应占据的宽度)。最后,根据titleLabel 的大小为按钮返回intrinsicContentSize,以便按钮完全包裹titleLabel

  1. 当按钮已经存在时调用被覆盖的layoutSubviews 布局并确定其框架大小。在它的第一步, 按钮的渲染宽度设置为preferredMaxLayoutWidth 按钮的titleLabel
  2. 第二步通过调用[super layoutSubviews]重新调用布局引擎,这样按钮intrinsicContentSize就是 根据titleLabel的重新确定 preferredMaxLayoutWidth,设置为按钮渲染宽度, 到现在为止。
  3. 在被覆盖的intrinsicContentSize 方法中,我们返回 完全包裹它的按钮的最小尺寸 titleLabelpreferredMaxLayoutWidth 设置。我们用 sizeThatFits 适合按钮的 titleLabel 方法,并且 就像titleLabel 一样工作,不遵循任何基于约束 布局。

结果应该类似于您可能需要的结果。

如有任何其他澄清/疑虑,请随时告诉我。

谢谢。

【讨论】:

  • 非常感谢您的解决方案。但是,我有一个问题,使用您的代码,按钮将有 4 行纵向,当我们旋转时,它仍然有 4 行横向,对吗?但是在我的图片帖子中,我的按钮纵向有 4 行,横向有 3 行。如果我错了纠正我。谢谢
  • 你在你的设备上试过了吗?我没有在真实设备上尝试过我的代码,但我已经在运行 iOS 9.2 的模拟器上尝试过。您可以在我更新的答案中看到每个方向的输出。该按钮应仅根据可用空间显示所需的行数。此外,我还为按钮层添加了一些边框以显示它的边界矩形。请告诉我。谢谢!
  • UIButton 的子类是我认为的好解决方案。如果您不想继承UIButton,您仍然可以创建包装视图。
  • 这个答案有一个小错误。 preferredMaxLayoutWidth 需要考虑 contentEdgeInsetstitleEdgeInsets 来计算正确的宽度。
  • @ClausJørgensen 你说得对。如果我们愿意,我们也可以考虑这些参数以及imageEdgeInsets。但我只是不想让我的答案变得复杂,只给出基本的想法。考虑到留给任何感兴趣的人的其他参数作为练习;)但这根本不是定义上的错误。
【解决方案3】:

Swift 中的 Ayan Sengupta 解决方案,支持 contentEdgeInsets(感谢 Claus Jørgensen):

(如果需要,您还可以进一步自定义代码以考虑titleEdgeInsets

  1. 子类化您的 UIButton 以获得布局过程的所有权:

     /// https://stackoverflow.com/a/50575588/1033581
     class AutoLayoutButton: UIButton {
         override var intrinsicContentSize: CGSize {
             var size = titleLabel!.sizeThatFits(CGSize(width: titleLabel!.preferredMaxLayoutWidth - contentEdgeInsets.left - contentEdgeInsets.right, height: .greatestFiniteMagnitude))
             size.height += contentEdgeInsets.left + contentEdgeInsets.right
             return size
         }
         override func layoutSubviews() {
             titleLabel?.preferredMaxLayoutWidth = frame.size.width
             super.layoutSubviews()
         }
     }
    
  2. 在情节提要中使用此类,并为Leading、Trailing、Top、Bottom 设置约束。但不要设置任何高度限制。

没有子类化的替代方法是按照Bartłomiej Semańczyk answerTimur Bernikowich comment 的建议添加包装器视图。

【讨论】:

  • 仅供参考,两个答案都有一个错误。 preferredMaxLayoutWidth 需要考虑 contentEdgeInsetstitleEdgeInsets 来计算正确的宽度。
  • @ClausJørgensen 谢谢,我已添加对contentEdgeInsets 的支持。关于titleEdgeInsets,其文档显示为“按钮不使用此属性来确定intrinsicContentSize”,因此我将忽略它作为我的答案,但请随时根据您的需要自定义代码。
【解决方案4】:

有一种我一直使用的方法:

  1. 添加另一个引用 UILabel,其中 lineNumber=0 并且与目标按钮的宽度相同。
  2. 不要为 ref-UILable 设置高度限制,应为按钮设置高度限制以调整其高度
  3. 使用 button.titleLable 为 ref UILabel 设置相同的文本,sizeTofit 并获取它的 frame.size.height
  4. 将高度值用于目标按钮的高度约束。 (当然,button.titleLabel linenumber 应该设置为 0 行或更多行)

完成。 :)

PS1。这种方式可以用于滚动视图中的按钮和引用标签。

PS2。在某些情况下,我们无法获得正确的参考标签高度,因为它无法在滚动视图中获得正确的 frame.width,尤其是当我们使用拖尾约束时。我们可以考虑在 sizeTofit 之前为 ref-label 定义一个固定的宽度,并获得目标按钮使用的正确高度。

【讨论】:

    猜你喜欢
    • 2014-04-22
    • 2020-07-18
    • 1970-01-01
    • 2018-01-14
    • 2017-04-03
    • 2016-01-09
    • 1970-01-01
    • 2014-07-17
    • 1970-01-01
    相关资源
    最近更新 更多