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