Multiline UIButton and autolayout Multiline UIButton and autolayout ios ios

Multiline UIButton and autolayout


I had the same problem where I wanted my button to grow along with its title. I had to sublcass the UIButton and its intrinsicContentSize so that it returns the intrinsic size of the label.

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

Since the UILabel is multiline, its intrinsicContentSize is unknown and you have to set its preferredMaxLayoutWidth See objc.io article about that

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

The rest of the layout should work. If you set your both button having equal heights, the other one will grow to. The complete button looks like this

@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


Swift 4.1.2 Version based on @Jan answer.

import UIKitclass 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 var intrinsicContentSize: CGSize {        get {             return titleLabel?.intrinsicContentSize ?? CGSize.zero        }    }    override func layoutSubviews() {        super.layoutSubviews()        titleLabel?.preferredMaxLayoutWidth = titleLabel?.frame.size.width ?? 0        super.layoutSubviews()    }}


This respects content edge insets and worked for me:

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