Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get layoutSubviews to be called before sizeThatFits?

When I call sizeToFit on one of my views, I noticed that the iOS framework first calls sizeThatFits and then layoutSubviews on that view. This is problematic for me because the calculation in my sizeThatFits depends on the results of my layoutSubviews. In the following example, a subview gets adjusted in layoutSubviews and this subview's height is used in sizeThatFits. Currently my code is not working the order is reversed.

In my view controller's viewDidLoad:

UIView* header = [[MyHeader alloc] init];
[header sizeToFit];
self.tableView.tableHeaderView = header;
[header release];

In my view:

- (void) layoutSubviews {
    [super layoutSubviews];
    [self.subView sizeToFit];
}

- (CGSize) sizeThatFits:(CGSize)size {
    return CGSizeMake(
        self.frame.size.width,
        self.subView.frame.size.height
    );
}
like image 469
Pwner Avatar asked Aug 27 '12 19:08

Pwner


2 Answers

You would want your view to always do The Right Thing(tm), no matter in which order you call things. To accomplish that, I would suggest putting your code that calculates the desired height for your view and the subviews in a separate method. Something along these lines:

- (void)updateSubviewDimensions
{
    [self.subview sizeToFit];
    // possibly much more code
}

- (void) layoutSubviews {
    [super layoutSubviews];
    [self updateSubviewDimensions];
}

- (CGSize) sizeThatFits:(CGSize)size {
    [self updateSubviewDimensions];
    return CGSizeMake(
        self.frame.size.width,
        self.subView.frame.size.height
    );
}

This way, you are not dependent on remembering to call a certain method before calling sizeToFit, but your view will always do what you want. Plus, your code is less brittle and easier to understand when you look at it a few months from now (or have a different developer look at it).

like image 80
Johannes Fahrenkrug Avatar answered Oct 04 '22 16:10

Johannes Fahrenkrug


If your sizeThatFits: requires layout to be accurate then you need to force a layout (see code below.) But I think this is poorly designed because your layout isn't taking the "size" into account.

- (CGSize) sizeThatFits:(CGSize)size {

    [self setNeedsLayout];
    [self layoutIfNeeded];

    return CGSizeMake(
        self.frame.size.width,
        self.subView.frame.size.height
    );
}

A better approach might be to use auto layout for your subviews, then use systemLayoutSizeFittingSize: to determine the required size of your view (vs. sizeThatFits:)

If you can't or don't want to use auto layout then you should consider having separate code paths for actual layout and layout calculation.

like image 45
TomSwift Avatar answered Oct 04 '22 16:10

TomSwift