IOS: UIButton 根据文本长度调整大小

在 Interface Builder 中,按住 Command + =键可以调整按钮的大小以适应其文本。我想知道在将按钮添加到视图之前,这是否可以通过编程方式实现。

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button.titleLabel setFont:[UIFont fontWithName:@"Arial-BoldMT" size:12]];
[button addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
// I need to know the width needed to accomodate NSStringVariable
[button setTitle:NSStringVariable forState:UIControlStateNormal];
// So that I can set the width property of the button before addSubview
[button setFrame:CGRectMake(10, 0, width, fixedHeight)];
[subNavigation addSubview:button];
174554 次浏览

在 UIKit 中,对 NSString 类进行了添加,以便从给定的 NSString 对象中获得它在以某种字体呈现时所占用的大小。

文档是 给你,现在是 给你

简而言之,如果你去:

CGSize stringsize = [myString sizeWithFont:[UIFont systemFontOfSize:14]];
//or whatever font you're using
[button setFrame:CGRectMake(10,0,stringsize.width, stringsize.height)];

按钮的框架设置为字符串的高度和宽度。

您可能想要在 CGSize 周围尝试一些缓冲区空间,但是您将从正确的位置开始。

在代码中实现这一点的方法是:

[button sizeToFit];

如果您正在子类化,并且想要添加额外的规则,您可以覆盖:

- (CGSize)sizeThatFits:(CGSize)size;

如果你的按钮是用 Interface Builder 做的,并且你正在用代码改变标题,你可以这样做:

[self.button setTitle:@"Button Title" forState:UIControlStateNormal];
[self.button sizeToFit];

我有一些额外的需求相关的这个职位,sizeToFit不解决。我需要保持按钮的中心在它的原始位置,不管文本。我也不希望按钮大于原来的尺寸。

因此,我布局按钮的最大大小,保存在控制器的大小,并使用下面的方法来调整按钮的大小时,文本变化:

+ (void) sizeButtonToText:(UIButton *)button availableSize:(CGSize)availableSize padding:(UIEdgeInsets)padding {


CGRect boundsForText = button.frame;


// Measures string
CGSize stringSize = [button.titleLabel.text sizeWithFont:button.titleLabel.font];
stringSize.width = MIN(stringSize.width + padding.left + padding.right, availableSize.width);


// Center's location of button
boundsForText.origin.x += (boundsForText.size.width - stringSize.width) / 2;
boundsForText.size.width = stringSize.width;
[button setFrame:boundsForText];
}

在 Xcode 4.5及以上版本中,现在可以使用“ 自动布局/约束”来完成这项工作。

主要优点是:

  1. 您根本不需要通过编程设置帧!
  2. 如果处理得当,就不需要为方向更改而重置框架。
  3. 此外,设备的更改不需要打扰您(阅读,不需要为不同的屏幕大小单独编写代码)。

一些缺点:

  1. 不向后兼容-仅适用于 iOS6及以上版本。
  2. 需要熟悉(但会节省以后的时间)。

最酷的是,我们可以专注于宣布一个意图,比如:

  • 我希望这两个按钮的宽度相同; 或
  • 我需要这个视图是垂直居中和扩展到一个最大的10分从超级视图的边缘; 甚至,
  • 我想这个按钮/标签调整大小根据它显示的标签!

这里 是一个介绍自动布局的简单教程。

对于 更多细节来说。

这需要一些时间在第一,但它肯定看起来将是很值得的努力。

如果您想调整文本的大小而不是按钮的大小,您可以使用..。

button.titleLabel.adjustsFontSizeToFitWidth = YES;
button.titleLabel.minimumScaleFactor = .5;
// The .5 value means that I will let it go down to half the original font size
// before the texts gets truncated


// note, if using anything pre ios6, you would use I think
button.titleLabel.minimumFontSize = 8;

斯威夫特:

这里重要的事情是 什么时候你会调用 .sizeToFit(),它应该在设置文本显示后,以便它可以读取所需的大小,如果你以后更新文本,然后再次调用它。

var button = UIButton()
button.setTitle("Length of this text determines size", forState: UIControlState.Normal)
button.sizeToFit()
self.view.addSubview(button)

简单来说:

  1. 创建 UIView作为包装器,自动布局到视图周围。
  2. UILabel放入封装器中,在封装器的边缘添加一些约束条件,这些约束条件将粘附在标签上。
  3. UIButton放入包装器中,然后简单地添加与 UILabel相同的约束。
  4. 享受你的自动化按钮与文字一起。

取而代之的是:

myButton.size = myButton.sizeThatFits(CGSize.zero)

你也可以把 contentInset加到按钮上:

myButton.contentEdgeInsets = UIEdgeInsetsMake(8, 8, 4, 8)

若要使用自动布局完成此操作,请尝试设置一个可变宽度约束:

Width Constraint

您可能还需要调整您的 Content Hugging PriorityContent Compression Resistance Priority,以获得您需要的结果。


UILabel 是 完全自动调整尺寸:

这个 UILabel 被简单地设置为以屏幕为中心(只有两个约束,水平/垂直) :

它完全自动改变宽度:

你不需要设置 任何的宽度或高度-它是完全自动的。

enter image description here

enter image description here

注意,黄色的小方块被简单地连接起来(“间距”为零)。当 UILabel 调整大小时,它们会自动移动。

添加“ > =”约束设置 UILabel 的最小宽度:

enter image description here

此按钮类具有文本的高度自动调整大小(对于 Xamarin,但可以为其他语言重写)

[Register(nameof(ResizableButton))]
public class ResizableButton : UIButton
{
NSLayoutConstraint _heightConstraint;
public bool NeedsUpdateHeightConstraint { get; private set; } = true;


public ResizableButton(){}


public ResizableButton(UIButtonType type) : base(type){}


public ResizableButton(NSCoder coder) : base(coder){}


public ResizableButton(CGRect frame) : base(frame){}


protected ResizableButton(NSObjectFlag t) : base(t){}


protected internal ResizableButton(IntPtr handle) : base(handle){}


public override void LayoutSubviews()
{
base.LayoutSubviews();
UpdateHeightConstraint();
InvalidateIntrinsicContentSize();
}


public override void SetTitle(string title, UIControlState forState)
{
NeedsUpdateHeightConstraint = true;
base.SetTitle(title, forState);
}


private void UpdateHeightConstraint()
{
if (!NeedsUpdateHeightConstraint)
return;
NeedsUpdateHeightConstraint = false;
var labelSize = TitleLabel.SizeThatFits(new CGSize(Frame.Width - TitleEdgeInsets.Left - TitleEdgeInsets.Right, float.MaxValue));


var rect = new CGRect(Frame.X, Frame.Y, Frame.Width, labelSize.Height + TitleEdgeInsets.Top + TitleEdgeInsets.Bottom);


if (_heightConstraint != null)
RemoveConstraint(_heightConstraint);


_heightConstraint = NSLayoutConstraint.Create(this, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1, rect.Height);
AddConstraint(_heightConstraint);
}


public override CGSize IntrinsicContentSize
{
get
{
var labelSize = TitleLabel.SizeThatFits(new CGSize(Frame.Width - TitleEdgeInsets.Left - TitleEdgeInsets.Right, float.MaxValue));
return new CGSize(Frame.Width, labelSize.Height + TitleEdgeInsets.Top + TitleEdgeInsets.Bottom);
}
}
}

Swift 4.2

谢天谢地,解决了。在将文本设置为按钮之后,可以从 UIView (官方文档是 给你)中检索 intrinsicContentSize,它是自然大小。 对于 UIButton,您可以像下面这样使用它。

button.intrinsicContentSize.width

告诉你,我调整了宽度,让它看起来更合适。

button.frame = CGRect(fx: xOffset, y: 0.0, width: button.intrinsicContentSize.width + 18, height: 40)

模拟器

带 ininsicContentSize 的 UIButtons

资料来源: https://riptutorial.com/ios/example/16418/get-uibutton-s-size-strictly-based-on-its-text-and-font

说实话,我觉得很遗憾在故事板中没有一个简单的复选框来说明你想要调整按钮的大小以适应文本。随便啦。

下面是使用故事板的最简单的解决方案。

  1. 放置 UIView 并为其设置约束。例如: enter image description here
  2. 将 UILabel 放置在 UIView 中。设置约束将其附加到 UIView 的边缘。

  3. 将您的 UIButton 放置在 UIView 中。设置相同的约束将其附加到 UIView 的边缘。

  4. 将 UILabel 的行数设置为0。 enter image description here

  5. 准备好电源插座。

    @IBOutlet var button: UIButton!
@IBOutlet var textOnTheButton: UILabel!
  1. 得到一些长,长,长的标题。

let someTitle = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  1. 在 viewDidLoad 上为 UILabel 和 UIButton 设置标题。

override func viewDidLoad() {
super.viewDidLoad()
textOnTheButton.text = someTitle
button.setTitle(someTitle, for: .normal)
button.titleLabel?.numberOfLines = 0
}
  1. 运行它,以确保按钮的大小调整,可以按下。

enter image description here

由于某种原因,func sizeToFit()对我不起作用。我的设置是,我使用一个按钮内的 UITableViewCell和我使用自动布局。

对我起作用的是:

  1. 得到宽度约束
  2. 得到的 intrinsicContentSize宽度,因为根据这个 文件自动布局没有意识到的 intrinsicContentSize。将宽度约束设置为 intrinsicContentSize宽度

enter image description here

这里有两个来自 Debug View Hierachry的标题 enter image description here

enter image description here

因为 sizeToFit将忽略 titleEdgeInsets设置。在我的例子中,按钮标题是固定的文本,所以我使用自动布局给每个按钮一个宽度约束。

我认为如果是动态文本长度,那么首先需要获得字符串大小,然后更新 viewDidLayoutSubview中的按钮约束。

    lazy var importButton: UIButton = {
let btn = UIButton()
btn.translatesAutoresizingMaskIntoConstraints = false
btn.setTitle("Import", for: .normal)
btn.titleLabel?.font = .systemFont(ofSize: 18, weight: .bold)
btn.setTitleColor(.white, for: .normal)
btn.titleEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
btn.backgroundColor = .myBlue
btn.layer.cornerRadius = 8
btn.clipsToBounds = true
btn.addTarget(self, action: #selector(importButtonTapped), for: .touchUpInside)
return btn
}()
    

lazy var stitchButton: UIButton = {
let btn = UIButton()
btn.translatesAutoresizingMaskIntoConstraints = false
btn.setTitle("Stitch", for: .normal)
btn.titleLabel?.font = .systemFont(ofSize: 18, weight: .bold)
btn.setTitleColor(.white, for: .normal)
btn.titleEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
btn.backgroundColor = .myGreen
btn.layer.cornerRadius = 8
btn.clipsToBounds = true
btn.addTarget(self, action: #selector(stitchButtonTapped), for: .touchUpInside)
return btn
}()


func setViews() {
title = "Image Stitcher"
view.backgroundColor = .systemBackground
        

view.addSubview(importButton)
view.addSubview(stitchButton)


let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
NSLayoutConstraint(item: importButton, attribute: .centerX, relatedBy: .equal, toItem: g, attribute: .trailing, multiplier: 0.3, constant: 0),
importButton.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -50),
importButton.widthAnchor.constraint(equalTo: g.widthAnchor, multiplier: 0.25),
importButton.heightAnchor.constraint(equalTo: importButton.widthAnchor, multiplier: 0.5),
            

NSLayoutConstraint(item: stitchButton, attribute: .centerX, relatedBy: .equal, toItem: g, attribute: .trailing, multiplier: 0.7, constant: 0),
stitchButton.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -50),
stitchButton.widthAnchor.constraint(equalTo: g.widthAnchor, multiplier: 0.25),
stitchButton.heightAnchor.constraint(equalTo: stitchButton.widthAnchor, multiplier: 0.5),
])
}

enter image description here

为了最终解决这个问题,虽然需要使它更现代化(即。IOS15及以上版本的按钮配置) ,只是字面上子类的 UIButton,像这样:

class AdaptableSizeButton: 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
}
}


试试看!

import UIKit


class ViewController: UIViewController {


let button = AdaptableSizeButton()
let v = UIView()
    

override func viewDidLoad() {
super.viewDidLoad()
        

view.translatesAutoresizingMaskIntoConstraints = false


button.setTitle("Ako ay may lobo lorem", for: .normal)
button.titleLabel?.textAlignment = .center
button.titleLabel?.lineBreakStrategy = .pushOut
button.titleEdgeInsets = .init(top: 0, left: 16, bottom: 0, right: 16)
button.backgroundColor = .red
        

view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
button.heightAnchor.constraint(equalToConstant: 50),
button.centerYAnchor.constraint(equalTo: view.centerYAnchor),
button.centerXAnchor.constraint(equalTo: view.centerXAnchor)
])
        

DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(3)) {
print("BOOM")
self.button.setTitle("aaaa bla  bla lumipad sa langit bbbb", for: .normal)
}
}
}

在我的情况下,sizeToFit()不工作,但我使用 UIFont.preferredFont(forTextStyle: _)的 UIButton 标题标签文本。设置 adjustsFontSizeToFitWidth修复了问题。

button.setTitle(" title ", for: .normal)
button.titleLabel?.font = UIFont.preferredFont(forTextStyle: .body)
button.titleLabel?.adjustsFontSizeToFitWidth = true