自适应titleLabel大小的UIButton

42

我有一个UIButton,我将其添加到故事板中的视图控制器的视图中。我添加居中约束来定位它,并添加前导空间约束来限制其宽度。在代码中,我添加:

self.button.titleLabel.numberOfLines = 0;
self.button.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
[self.button setTitle:@"A real real real real real real real real long long name." forState:UIControlStateNormal];
self.button.backgroundColor = [UIColor redColor];
self.button.titleLabel.backgroundColor = [UIColor blueColor];
下面显示结果: enter image description here 我希望按钮根据其内容大小调整大小。我该如何做?
我已经尝试过
[self.button sizeToFit];

我已经尝试将内容抱紧和压缩阻力自动布局约束的优先级设置为必需。

我还尝试了显式地设置 contentEdgeInsetstitleEdgeInsetsUIEdgeInsetsZero 并调用 invalidateIntrinsicContentSize

我还注意到,如果在标题字符串中放置换行符,则按钮似乎会调整大小以适应其内容。

我正在 iPhone 6 模拟器上运行 Xcode 6 和 iOS 8。

13个回答

43

Swift 4.x版本的Kubba的回答:

需要在界面构建器中将换行更新为相应的裁剪/自动换行/,并对应到相应的按钮。

class ResizableButton: UIButton {
    override var intrinsicContentSize: CGSize {
       let labelSize = titleLabel?.sizeThatFits(CGSize(width: frame.width, height: .greatestFiniteMagnitude)) ?? .zero
       let desiredButtonSize = CGSize(width: labelSize.width + titleEdgeInsets.left + titleEdgeInsets.right, height: labelSize.height + titleEdgeInsets.top + titleEdgeInsets.bottom)

       return desiredButtonSize
    }
}

3
对我没用:titleLabel 仍然超出了 UIButton 的边框。 - xaphod
@xaphod - 在属性检查器中忘记设置子类了吗? - Ahmed Khedr
它可以工作,但你应该实现 override func setTitle(_ title: String?, for state: UIControl.State) { super.setTitle(title, for: state)layoutIfNeeded()} - Daniel Kuta
只需要稍作修改:let with = labelSize.width + (image(for:[])?.size.width)! + titleEdgeInsets.left + titleEdgeInsets.right + imageEdgeInsets.left + imageEdgeInsets.right + contentEdgeInsets.left + contentEdgeInsets.right - HaZe
在我的情况下,frame.width == 0.0。这会导致错误的结果(当多行文本的高度正确时,只显示一行文本的高度)。我必须从其他地方获取合适的宽度。因此,如果这种方法不起作用,请检查frame.width的值。 - Gerd Castan
显示剩余2条评论

23

我已经让它工作了,但你必须使用自定义按钮,而不是系统类型。给按钮添加宽度和高度约束,并创建一个 IBOutlet 来引用高度约束(在我的代码中是heightCon),这样你就可以在代码中调整它。

- (void)viewDidLoad {
    [super viewDidLoad];
    self.button.titleLabel.numberOfLines = 0;
    self.button.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
    [self.button setTitle:@"A real real real real real real real real long long name." forState:UIControlStateNormal];
    [self.button addTarget:self action:@selector(doStuff:) forControlEvents:UIControlEventTouchUpInside];
    self.button.backgroundColor = [UIColor redColor];
    self.button.titleLabel.backgroundColor = [UIColor blueColor];
    [self.button layoutIfNeeded]; // need this to update the button's titleLabel's size
    self.heightCon.constant = self.button.titleLabel.frame.size.height;
}

编辑后:

我发现如果你创建一个子类,并使用以下代码,你可以更简单地使用系统按钮完成这个操作。

@implementation RDButton

-(CGSize)intrinsicContentSize {
    return CGSizeMake(self.frame.size.width, self.titleLabel.frame.size.height);
}

在设置标题时,会调用覆盖的intrinsicContentSize方法。在这种情况下,不应该设置高度约束。


23

我遇到了同样的问题。只有当UIButtontitleLabel有多行时才会出现。这是UIKit中的一个bug吗?

我的Swift解决方案:


class ResizableButton: UIButton {    
    override var intrinsicContentSize: CGSize {
        let labelSize = titleLabel?.sizeThatFits(CGSize(width: frame.size.width, height: CGFloat.greatestFiniteMagnitude)) ?? .zero
        let desiredButtonSize = CGSize(width: labelSize.width + titleEdgeInsets.left + titleEdgeInsets.right, height: labelSize.height + titleEdgeInsets.top + titleEdgeInsets.bottom)

        return desiredButtonSize
    }
}

1
我们如何为这个变化添加动画效果? - Alexander Khitev
1
@AlexanderKhitev 在 UIView.animation 块中可以调用 invalidateIntrinsicContentSize() - Sergei Navka

7

我曾经为此苦苦挣扎,最终通过子类化UIButton并添加以下两个函数使其正常工作。

class GoalsButton: UIButton {

    override var intrinsicContentSize: CGSize {
        return self.titleLabel!.intrinsicContentSize
    }

    // Whever the button is changed or needs to layout subviews,
    override func layoutSubviews() {
        super.layoutSubviews()
        titleLabel?.preferredMaxLayoutWidth = self.titleLabel!.frame.size.width
    }
}

2

我现在离开了电脑,无法立即添加代码,但我之前已经找到了解决方法。

你可以创建一个 UILabel 并为该标签添加 UITapGestureRecognizer。通过处理触摸事件来执行所需的按钮操作。同时确保在 UILabel 上启用用户交互。

这个标签现在会像一个自动调整大小的按钮一样工作。


这个在尺寸方面完全符合我的要求,而且这也是我最先尝试的方法,但我想要当用户触摸它时,按钮具有文本高亮行为。 - Darren
嘿,达伦,您可以设置 UILabel.textColorUILabel.highlightedTextColor 属性。稍后,您可以更新 UILabel.highlighted 属性以更改文本颜色(例如根据 UITapGestureRecognizer 状态)。 [Gist](https://gist.github.com/vgorloff/373e4e98ffb81c0a0117) - Vlad

1
我建议的是计算文本的宽度,并自行计算框架。这并不复杂,首先获取文本的宽度:
[NSString sizeWithFont:font];

进行模运算,你就可以轻松地找出文本的行数。

请注意,此方法适用于iOS7之前的版本,对于iOS 7及更高版本,您可能需要尝试

[NSString sizeWithAttributes:aDictionary];

1

当视图被布局时,我不得不使内在内容大小无效,并计算按钮的高度以获取intrinsicContentSize属性。

以下是Swift3 / Xcode 9中的代码

override func layoutSubviews() {
    self.invalidateIntrinsicContentSize()
    super.layoutSubviews()
}


override var intrinsicContentSize: CGSize {
    return CGSize(width: self.frame.size.width, height: titleLabel!.frame.size.height + contentEdgeInsets.top + contentEdgeInsets.bottom)
}

1

请按照以下方式覆盖自定义UIButton中的-(CGSize)intrinsicContentSize。

Objective-C:

-(CGSize)intrinsicContentSize {
    CGSize titleLabelIntrinsicSize = [self.titleLabel intrinsicContentSize];
    return CGSizeMake(titleLabelIntrinsicSize.width + self.contentEdgeInsets.left + self.contentEdgeInsets.right, titleLabelIntrinsicSize.height + self.contentEdgeInsets.top + self.contentEdgeInsets.bottom);
}

Swfit :

override var intrinsicContentSize: CGSize {
        get {
            if let thisSize = self.titleLabel?.intrinsicContentSize {
                return CGSize(width: thisSize.width + self.contentEdgeInsets.left + self.contentEdgeInsets.right, height: thisSize.height + self.contentEdgeInsets.top + self.contentEdgeInsets.bottom)
            }
            return super.intrinsicContentSize
        }
    }

1
我曾经遇到过带有多行文本和图片的UIButton同样的问题。我使用sizeThatFits:计算大小,但是它计算出来的高度是错误的。
我没有将其设置为UIButtonTypeCustom,而是在按钮的titleLabel上调用了sizeThatFits:,并使用较小的宽度(由于按钮中的图像)。
CGSize buttonSize = [button sizeThatFits:CGSizeMake(maxWidth, maxHeight)];
CGSize labelSize = [button.titleLabel sizeThatFits:CGSizeMake(maxWidth - offset, maxHeight)]; // offset for image
buttonSize.height = labelSize.height;
buttonFrame.size = buttonSize;

然后我使用该尺寸的高度来正确设置按钮的框架,它起作用了 :)

也许他们在UIButton的内部大小调整上有一些bug。


1
如果您的Autolayout已关闭,则[self.button sizeToFit]应该能够正常工作。如果必须使用autolayout,则其他建议(计算行宽等)更为适当。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接