UIButton调整大小以适应其titleLabel

时间:2014-12-02 21:30:18

标签: ios uibutton uilabel autolayout

我有一个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];

我尝试将内容拥抱和压缩阻力自动布局约束优先级设置为所需。

我还尝试明确将contentEdgeInsetstitleEdgeInsets设置为UIEdgeInsetsZero并调用invalidateIntrinsicContentSize

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

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

12 个答案:

答案 0 :(得分:22)

Kubba's的Swift 4.x 版本答案:

需要在“界面”构建器中将换行符更新为剪辑/ WordWrap / 到相应的按钮。

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
    }
}

答案 1 :(得分:18)

我已经开始使用它,但您必须使用自定义按钮,而不是系统类型。给按钮提供宽度和高度约束,并将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方法。在这种情况下,您不应该设置高度约束。

答案 2 :(得分:3)

我挣扎了一段时间,最后通过继承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
    }
}

答案 3 :(得分:2)

我离开了我的电脑,所以我现在无法添加代码,但我之前找到了解决方法。

您可以做的是创建UILabel并在标签上添加UITapGestureRecognizer。通过处理点击事件,为按钮的操作执行任何操作。并确保在UILabel上启用用户互动。

此标签现在将作为自动调整大小按钮。

答案 4 :(得分:1)

我建议的是计算文本的宽度,并自己计算框架。无论如何,它并不复杂,首先得到文本的宽度:

[NSString sizeWithFont:font];

执行mod操作,您可以轻松找到文本的行数。

请注意,此方法适用于iOS7之前,适用于iOS 7以及之后您可能需要尝试

[NSString sizeWithAttributes:aDictionary];

答案 5 :(得分:1)

如果您的Autolayout已关闭,

[self.button sizeToFit]应该有效。如果你必须使用自动布局,那么其他建议(计算线宽)等更合适。

答案 6 :(得分:1)

我遇到了带有多行文字的UIButton同样的问题,而且还有一张图片。我使用sizeThatFits:来计算尺寸,但计算出错误的高度。

我没有将其设为UIButtonTypeCustom,而是在按钮的sizeThatFits:上调用了titleLabel,其宽度更小(由于按钮中的图像):

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的内部大小调整中存在一些错误。

答案 7 :(得分:1)

覆盖自定义UIButton中的 - (CGSize)intrinsicContentSize,如下所示。

目标 - 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
        }
    }

答案 8 :(得分:1)

/*
In SWIFT : Create an IBDesignable sub class of UIButton and override the intrinsicContentSize as shown below. It will resize appropriately for text and images. Then change your line break property to Word Wrap. Add a "fixedWidth" inspectable property that will adjust the height if you need buttons with fixed widths or when set to false will adjust the width and keep the height fixed.
*/

import UIKit

@IBDesignable
class UIButtonX: UIButton {
    
    //...

    @IBInspectable var fixedWidth : Bool = true
override var intrinsicContentSize: CGSize {
    let labelSize = titleLabel?.sizeThatFits(CGSize(width: fixedWidth ? frame.width : .greatestFiniteMagnitude, height: fixedWidth ? .greatestFiniteMagnitude : frame.height)) ?? .zero

    let wImage = image(for: [])?.size.width ?? 0
    let wTitleInset = titleEdgeInsets.left + titleEdgeInsets.right
    let wImageInset = imageEdgeInsets.left + imageEdgeInsets.right
    let wContentInset = contentEdgeInsets.left + contentEdgeInsets.right
    let width : CGFloat = labelSize.width + wImage + wTitleInset + wImageInset + wContentInset
    
    let biggerHeight = max(image(for: [])?.size.height ?? 0, labelSize.height)
    let hTitleInset = titleEdgeInsets.top + titleEdgeInsets.bottom
    let hImageInset = imageEdgeInsets.top + imageEdgeInsets.bottom
    let hContentInset = contentEdgeInsets.top + contentEdgeInsets.bottom
    let height : CGFloat = biggerHeight + hTitleInset + hImageInset + hContentInset
    
    let desiredButtonSize = CGSize(width: width, height: height)
    
    return desiredButtonSize
}
    
    //...

}

答案 9 :(得分:0)

在布局视图时,我必须使内在内容大小无效,然后计算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)
}

答案 10 :(得分:0)

我创建了一个在 UITableViewCell 中运行良好的子类。如果您想要例如屏幕截图中的按钮周围的边框,则需要它

UIButton with line breaks and a border

import Foundation
import UIKit

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

       return desiredButtonSize
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        invalidateIntrinsicContentSize()
    }
    
    func setup() {
        titleLabel?.lineBreakMode = .byWordWrapping
    }
    
    override func setTitle(_ title: String?, for state: UIControl.State) {
        super.setTitle(title, for: state)
        layoutIfNeeded()
    }
    
}

答案 11 :(得分:-1)

class SFResizableButton: UIButton {
  override var intrinsicContentSize: CGSize {
    get {
      var labelSize = CGSize.zero
        if let text = titleLabel?.text, let font = titleLabel?.font {
          labelSize.width = text.width(constrained: .greatestFiniteMagnitude, font: font)
        } else if let att = titleLabel?.attributedText {
          labelSize.width = att.width(constrained: .greatestFiniteMagnitude)
        }
      if let imageView = imageView {
        labelSize.width = labelSize.width + imageView.frame.width
      }
      let desiredButtonSize = CGSize(width: ceil(labelSize.width) + titleEdgeInsets.left + titleEdgeInsets.right + imageEdgeInsets.left + imageEdgeInsets.right, height: labelSize.height + titleEdgeInsets.top + titleEdgeInsets.bottom + imageEdgeInsets.top + imageEdgeInsets.bottom)

        return desiredButtonSize
    }
  }
}
extesion String {
  func width(constrained height: CGFloat, font: UIFont) -> CGFloat {
    let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
      let boundingBox = (self as NSString).boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)

      return boundingBox.width
  }
}

extension NSAttributedString {
  func width(constrained height: CGFloat) -> CGFloat {
    let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
      let boundingBox = boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, context: nil)

      return boundingBox.width
  }
}