I have a UIButton
and it can change the title at the runtime. Therefore, I want to increase the UIButton
height depend on the title text for display full text by using AutoLayout
.
I can increase the UILabel
height by set the height constraint
to "Greater than or Equal" but it not work with UIButton
.
I have used [myButton sizeToFit]
but it only increase the UIButon
width (not increase height).
My current UIButton
properties now is
- constraint height: 30
- leading : 15
- trailing: 15
- top: 5
- fontsize: 12
UPDATE
I created an IBOutlet for constraint height of UIButton
for changing the height as @NSNood
said.
Then I need to use \n
in title text to split line.
But I don't know where should I put the \n
?
Here is the Button
that I want in portrait
Here is the Button
that I want in landscape
How can I determine the place to put \n
?
Please guide me how to achieve it with AutoLayout
. Any help would be appreciated.
Sorry that I didn't follow the post, lately and thus am coming up with a real late solution. Still I'm writing the answer as a reference, if someone might find it useful in future.
First of all let's show the storyboard configuration for the button. Those are depicted in the following pictures:
The picture shows that I have added only left, top and right constraints for the button and nothing else. This allows the button to have some intrinsicContentSize
for it's height but it's width is still determined by it's left and right constraints.
The next phase is to write some ViewController class that shall contain the button. In my VC, I have created an outlet for the button by name button
:
@property(nonatomic,weak) IBOutlet UIButton* button;
and has attached it to the storyboard button. Now I have overridden two methods, namely, viewDidLoad
and viewWillLayoutSubviews
like below:
-(void)viewDidLoad {
[super viewDidLoad];
self.button.titleLabel.numberOfLines = 0;
self.button.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
}
-(void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
[self.button setTitle:@"Chapter One\n "
"A Stop on the Salt Route\n "
"1000 B.C.\n "
"As they rounded a bend in the path that ran beside the river, Lara recognized the silhouette of a fig tree atop a nearby hill. The weather was hot and the days were long. The fig tree was in full leaf, but not yet bearing fruit." forState:UIControlStateNormal];
}
viewDidLoad
method ensures the titleLabel
(the label that
holds button text) is multiline and if some large text comes to it,
it wraps the text by wrapping words.viewWillLayoutSubviews
method ensures button layouting process
occurs whenever bounds of the main view change, e.g. due to the
change of interface orientation.The final and the most effective part is to manually handle the layout process for the button. For this purpose, we need to subclass UIButton
. I have written a subclass named MyButton
that inherits from UIButton
and you might use whatever name you like. Set this as the custom class for the button in Identity Inspector.
The subclass overrides two methods, namely, intrinsicContentSize
and layoutSubviews
. The class body looks something like the following:
#import "MyButton.h"
@implementation MyButton
-(CGSize)intrinsicContentSize {
return [self.titleLabel sizeThatFits:CGSizeMake(self.titleLabel.preferredMaxLayoutWidth, CGFLOAT_MAX)];;
}
-(void)layoutSubviews {
self.titleLabel.preferredMaxLayoutWidth = self.frame.size.width;
[super layoutSubviews];
}
@end
The UIButon
subclass takes the ownership of the layout process by overriding layoutSubviews
method. The basic idea here is to determine the button width, once it has been layout. Then setting the width as preferredMaxLayoutWidth
(the maximum width for layouting engine, that a multiline label should occupy) of it's child titleLabel
(the label that holds button text). Finally, returning an intrinsicContentSize
for the button based on it's titleLabel
's size, so that the button fully wraps it's titleLabel
.
layoutSubviews
is called when the button is already
layed out and it's frame size is determined. At it's first step,
button's rendered width is set as preferredMaxLayoutWidth
of the
button's titleLabel
.[super
layoutSubviews]
, so that the buttons intrinsicContentSize
is
re-determined based on it's titleLabel
's
preferredMaxLayoutWidth
, which is set to buttons rendered width,
by now.intrinsicContentSize
method we return the
minimum fitting size for the button that fully wraps it's
titleLabel
with preferredMaxLayoutWidth
set. We use
sizeThatFits
fits method on the button's titleLabel
and that
simply works as titleLabel
doesn't follow any constraint based
layout.The outcome should be something similar to that you might have required.
Feel free to let me know about any other clarification/concern.
Thanks.
Ayan Sengupta solution in Swift, with support for contentEdgeInsets (thanks Claus Jørgensen):
(You may also further customize the code to take titleEdgeInsets
into account if needed)
Subclass your UIButton to take the ownership of the layout process:
/// https://stackoverflow.com/a/50575588/1033581
class AutoLayoutButton: UIButton {
override var intrinsicContentSize: CGSize {
var size = titleLabel!.sizeThatFits(CGSize(width: titleLabel!.preferredMaxLayoutWidth - contentEdgeInsets.left - contentEdgeInsets.right, height: .greatestFiniteMagnitude))
size.height += contentEdgeInsets.left + contentEdgeInsets.right
return size
}
override func layoutSubviews() {
titleLabel?.preferredMaxLayoutWidth = frame.size.width
super.layoutSubviews()
}
}
Use this class in your storyboard, and set constraints for Leading, Trailing, Top, Bottom. But don't set any Height constraint.
An alternative without subclassing is to add a wrapper view as suggested by Bartłomiej Semańczyk answer and Timur Bernikowich comment.
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