IOS: Adjust UIButton height depend on title text using Autolayout?

前端 未结 4 1820
囚心锁ツ
囚心锁ツ 2021-01-02 12:40

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 displa

4条回答
  •  野趣味
    野趣味 (楼主)
    2021-01-02 13:17

    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];
    }
    
    1. The 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.
    2. The 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.

    1. The overridden 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.
    2. The second step re-invokes the layouting engine by calling [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.
    3. In the overridden 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.

提交回复
热议问题