IOS: setting UIButton height depends on header text using Autolayout?

I have a UIButton and it can change the title at runtime. So I want to increase the height of the UIButton depending on the title text to display the full text using AutoLayout .

I can increase the height of the UILabel by setting the height constraint to Greater Than or UIButton , but it doesn’t work with UIButton .
I used [myButton sizeToFit] , but increased only the width of the UIButon (without increasing the height).

Now my current UIButton properties are restriction height: 30 - leading: 15 - trailing: 15 - Top 5 - fontsize: 12

UPDATE
I created an IBOutlet for height restrictions of UIButton for changing height as @NSNood . Then I need to use \n in the header text to split the line.
But I do not know where I should put \n ?

Here is the Button I want in the portrait

enter image description here

Here is the Button I want in landscape enter image description here

How to determine the location for \n ?

Tell us how to do it with AutoLayout . Any help would be greatly appreciated.

+6
source share
3 answers

Sorry that I have not followed this post lately and thus came up with a real late version. However, I am writing the answer as a link, if someone might find it useful in the future.

First of all, let it show the storyboard configuration for the button. They are depicted in the following figures:

Constraint Configuration

The figure shows that I added only the buttons on the left, top and right for the button and nothing more. This allows the button to have an intrinsicContentSize value for its height, but the width is still determined by its left and right constraints.

The next step is to write some ViewController class, which should contain a button. In my VC, I created an output for a button named button :

 @property(nonatomic,weak) IBOutlet UIButton* button; 

and attached it to the storyboard button. Now I have overridden two methods, namely viewDidLoad and viewWillLayoutSubviews , as shown 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 BC\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]; } 
  • The viewDidLoad method provides the titleLabel (the label that contains the button text) is multi-line, and if any large text comes to it, it wraps the text, wrapping the words.
  • The viewWillLayoutSubviews method provides the button layout process that occurs when the borders of the main view change, for example. due to a change in the orientation of the interface.

The final and most effective part is the manual processing of the layout process for the button. For this we need a subclass of UIButton . I wrote a subclass called MyButton , which inherits from UIButton , and you can use whatever name you like. Set this as the custom class for the button in the Identity Inspector.

The subclass overrides two methods: intrinsicContentSize and layoutSubviews . The class body looks something like this:

 #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 responsibility for the layout process by overriding the layoutSubviews method. The main idea here is to determine the width of the button as soon as it was the layout. Then set the width as preferredMaxLayoutWidth (the maximum width for the layout mechanism that the multi-line label should take) from it child titleLabel (the label on which the button text is stored). Finally, returning intrinsicContentSize for the button based on this titleLabel size titleLabel that the button completely titleLabel its titleLabel .

  • An overridden layoutSubviews when the button is already laid out and the frame size is determined. In this first step, the width of the displayed button is set as the preferredMaxLayoutWidth titleLabel button.
  • The second step re-calls the layout engine, calling [super layoutSubviews] , so that the intrinsicContentSize buttons are redefined based on this titleLabel preferredMaxLayoutWidth , which is currently set to the display width of the buttons.
  • In the overridden intrinsicContentSize method, we return the minimum fitting size for the button that completely wraps the titleLabel with the preferredMaxLayoutWidth set. We use sizeThatFits sets the method on the titleLabel button and that just works as titleLabel does not follow any restrictions based on location.

The result should be something similar to what you might have demanded.

Portrait Landscape

Feel free to inform me of any other clarifications / concerns.

Thanks.

+8
source

The fact is that if you set the sizeToFit property, the text will always be on one line, and the button width will increase if you do not place the next line \n to explicitly say what you want for several lines.

You put '\ n' at the end of the first line, for example "line \n line" , which represents

 line line 

If you want to have two different string values ​​(with \n located differently) for Portrait and Landscape , you can check the orientation status using the UIDeviceOrientation ( UIDevice.currentDevice.orientation ) described here and set the string value depending on the orientation of the device

+1
source

Ayan Sengupta's solution in Swift:

  1. Subclass your UIButton to take responsibility for the layout process:

     /// https://stackoverflow.com/a/50575588/1033581 class AutoLayoutButton: UIButton { override var intrinsicContentSize: CGSize { return titleLabel!.sizeThatFits(CGSize(width: titleLabel!.preferredMaxLayoutWidth, height: .greatestFiniteMagnitude)) } override func layoutSubviews() { titleLabel?.preferredMaxLayoutWidth = frame.size.width super.layoutSubviews() } } 
  2. Use this class in your storyboard and set limits for Leading, Trailing, Top, Bottom. But do not set a height limit.

An alternative without subclasses is to add a wrapper view, as suggested by Bartłomiej Semańczyk's answer and Timur Bernikovic's commentary .

+1
source

Source: https://habr.com/ru/post/1239773/


All Articles