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 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 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
}
}
A simple solution working for me: make the multiline button to respect its title height in Swift 4.2 by adding a constraint for the button's height based on its title label's height:
let height = NSLayoutConstraint(item: multilineButton,
attribute: .height,
relatedBy: .equal,
toItem: multilineButton.titleLabel,
attribute: .height,
multiplier: 1,
constant: 0)
multilineButton.addConstraint(height)
add the missing constraints:
if let label = button.titleLabel {
button.addConstraint(NSLayoutConstraint(item: label, attribute: .top, relatedBy: .equal, toItem: button, attribute: .top, multiplier: 1.0, constant: 0.0))
button.addConstraint(NSLayoutConstraint(item: label, attribute: .bottom, relatedBy: .equal, toItem: button, attribute: .bottom, multiplier: 1.0, constant: 0.0))
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With