多行UIButton和自动布局

我创build了一个看起来像这样的视图控制器:

在这里输入图像说明

我希望两个顶部的button始终有20个点在他们和整个视图的左/右边缘之间。 它们也应该始终具有相同的宽度。 我已经创造了所有这些限制,它的工作原理是我想要的。 问题是垂直约束。 button应始终在顶边下20个点。 他们应该有相同的高度。 但是,autolayout并不尊重左标签需要两行来适应所有文本,所以结果如下所示:

在这里输入图像说明

我希望它看起来像在第一张照片。 我不能将恒定的高度约束添加到button,因为当应用程序在iPad上运行时,只需要一条线,那么就有了额外的空间。

viewDidLoad我试过这个:

 - (void)viewDidLoad { [super viewDidLoad]; self.leftButton.titleLabel.preferredMaxLayoutWidth = (self.view.frame.size.width - 20.0 * 3) / 2.0; self.rightButton.titleLabel.preferredMaxLayoutWidth = (self.view.frame.size.width - 20.0 * 3) / 2.0; } 

但是这根本没有改变。

问题:如何使左侧button需要两行自动布局?

我有同样的问题,我想让我的button与其标题一起成长。 我不得不sublcass UIButton及其intrinsicContentSize以便它返回标签的固有大小。

 - (CGSize)intrinsicContentSize { return self.titleLabel.intrinsicContentSize; } 

由于UILabel是多行,其intrinsicContentSize是未知的,你必须设置其preferredMaxLayoutWidth 请参阅objc.io文章

 - (void)layoutSubviews { [super layoutSubviews]; self.titleLabel.preferredMaxLayoutWidth = self.titleLabel.frame.size.width; [super layoutSubviews]; } 

其余的布局应该工作。 如果你把两个button的高度相等,那么另一个button就会长到。 完整的button看起来像这样

 @implementation TAButton - (instancetype)initWithCoder:(NSCoder *)coder { self = [super initWithCoder:coder]; if (self) { self.titleLabel.numberOfLines = 0; self.titleLabel.lineBreakMode = NSLineBreakByWordWrapping; } return self; } - (CGSize)intrinsicContentSize { return self.titleLabel.intrinsicContentSize; } - (void)layoutSubviews { [super layoutSubviews]; self.titleLabel.preferredMaxLayoutWidth = self.titleLabel.frame.size.width; [super layoutSubviews]; } @end 

基于@Jan的Swift版本的答案。

 import UIKit class MultiLineButton: UIButton { // MARK: - Init required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.commonInit() } private func commonInit() { self.titleLabel?.numberOfLines = 0 self.titleLabel?.lineBreakMode = .ByWordWrapping } // MARK: - Overrides override func intrinsicContentSize() -> CGSize { return titleLabel?.intrinsicContentSize() ?? CGSizeZero } override func layoutSubviews() { super.layoutSubviews() titleLabel?.preferredMaxLayoutWidth = titleLabel?.frame.size.width ?? 0 super.layoutSubviews() } } 

你有没有尝试过使用这个:

 self.leftButton.titleLabel.textAlignment = NSTextAlignmentCenter; self.leftButton.titleLabel.lineBreakMode = NSLineBreakByWordWrapping | NSLineBreakByTruncatingTail; self.leftButton.titleLabel.numberOfLines = 0; 

更新了基于@ Jan的答案的Swift / Swift 2.0版本

 @IBDesignable class MultiLineButton:UIButton { //MARK: - //MARK: Setup func setup () { self.titleLabel?.numberOfLines = 0 //The next two lines are essential in making sure autolayout sizes us correctly self.setContentHuggingPriority(UILayoutPriorityDefaultLow+1, forAxis: .Vertical) self.setContentHuggingPriority(UILayoutPriorityDefaultLow+1, forAxis: .Horizontal) } //MARK:- //MARK: Method overrides required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) setup() } override init(frame: CGRect) { super.init(frame: frame) setup() } override func intrinsicContentSize() -> CGSize { return self.titleLabel!.intrinsicContentSize() } override func layoutSubviews() { super.layoutSubviews() titleLabel?.preferredMaxLayoutWidth = self.titleLabel!.frame.size.width } } 

调整Swift 3.1

intrisicContentSize是一个属性而不是一个函数

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

Swift 3中的完整课程 – 基于@Jan,@Quantaliinuxite和@matt bezark:

 @IBDesignable class MultiLineButton:UIButton { //MARK: - //MARK: Setup func setup () { self.titleLabel?.numberOfLines = 0 //The next two lines are essential in making sure autolayout sizes us correctly self.setContentHuggingPriority(UILayoutPriorityDefaultLow+1, for: .vertical) self.setContentHuggingPriority(UILayoutPriorityDefaultLow+1, for: .horizontal) } //MARK:- //MARK: Method overrides required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) setup() } override init(frame: CGRect) { super.init(frame: frame) setup() } override var intrinsicContentSize: CGSize { return self.titleLabel!.intrinsicContentSize } override func layoutSubviews() { super.layoutSubviews() titleLabel?.preferredMaxLayoutWidth = self.titleLabel!.frame.size.width } } 

@ Jan的答案不适用于我(至less)在iOS 8.1,9.0与Xcode 9.1。 问题: titleLabel-intrinsicContentSize返回非常大的宽度和小的高度,因为根本没有宽度限制( titleLabel.frame on call具有零大小,导致测量问题)。 此外,它不考虑可能的插页和/或图像。

所以,这里是我的实现,应该修复所有的东西(只有一个方法是非常必要的):

 @implementation PRButton - (CGSize)intrinsicContentSize { CGRect titleFrameMax = UIEdgeInsetsInsetRect(UIEdgeInsetsInsetRect(UIEdgeInsetsInsetRect( self.bounds, self.alignmentRectInsets), self.contentEdgeInsets), self.titleEdgeInsets ); CGSize titleSize = [self.titleLabel sizeThatFits:CGSizeMake(titleFrameMax.size.width, CGFLOAT_MAX)]; CGSize superSize = [super intrinsicContentSize]; return CGSizeMake( titleSize.width + (self.bounds.size.width - titleFrameMax.size.width), MAX(superSize.height, titleSize.height + (self.bounds.size.height - titleFrameMax.size.height)) ); } @end 

这尊重内容边缘插件,并为我工作:

 class MultilineButton: UIButton { func setup() { self.titleLabel?.numberOfLines = 0 self.setContentHuggingPriority(UILayoutPriorityDefaultLow + 1, for: .vertical) self.setContentHuggingPriority(UILayoutPriorityDefaultLow + 1, for: .horizontal) } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) setup() } override init(frame: CGRect) { super.init(frame: frame) setup() } override var intrinsicContentSize: CGSize { let size = self.titleLabel!.intrinsicContentSize return CGSize(width: size.width + contentEdgeInsets.left + contentEdgeInsets.right, height: size.height + contentEdgeInsets.top + contentEdgeInsets.bottom) } override func layoutSubviews() { super.layoutSubviews() titleLabel?.preferredMaxLayoutWidth = self.titleLabel!.frame.size.width } }