Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<h3>The Autolayout Question</h3> <p>In the basic case you mentioned, you can get the correct size by calling <code>setNeedsLayout</code> and then <code>layoutIfNeeded</code> on the container view.</p> <p>From the <a href="https://developer.apple.com/library/ios/documentation/uikit/reference/uiview_class/uiview/uiview.html#//apple_ref/occ/instm/UIView/layoutIfNeeded">UIView class reference on <code>layoutIfNeeded</code></a>:</p> <blockquote> <p>Use this method to force the layout of subviews before drawing. Starting with the receiver, this method traverses upward through the view hierarchy as long as superviews require layout. Then it lays out the entire tree beneath that ancestor. Therefore, calling this method can potentially force the layout of your entire view hierarchy. The UIView implementation of this calls the equivalent CALayer method and so has the same behavior as CALayer.</p> </blockquote> <p>I don't think the "entire view hierarchy" applies to your use case since the metrics view presumably wouldn't have a superview.</p> <h3>Sample Code</h3> <p>In a sample empty project, with just this code, the correct frame is determined after <code>layoutIfNeeded</code> is called:</p> <pre><code>#import "ViewController.h" @interface ViewController () @property (nonatomic, strong) UIView *redView; @end @implementation ViewController @synthesize redView; - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. redView = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 220, 468)]; redView.backgroundColor = [UIColor redColor]; redView.translatesAutoresizingMaskIntoConstraints = NO; [self.view addSubview:redView]; NSLog(@"Red View frame: %@", NSStringFromCGRect(redView.frame)); // outputs "Red View frame: {{50, 50}, {220, 468}}" [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[redView(==100)]" options:0 metrics:Nil views:NSDictionaryOfVariableBindings(redView)]]; [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-100-[redView(==100)]" options:0 metrics:Nil views:NSDictionaryOfVariableBindings(redView)]]; NSLog(@"Red View frame: %@", NSStringFromCGRect(redView.frame)); // outputs "Red View frame: {{50, 50}, {220, 468}}" [self.view setNeedsLayout]; NSLog(@"Red View frame: %@", NSStringFromCGRect(redView.frame)); // outputs "Red View frame: {{50, 50}, {220, 468}}" [self.view layoutIfNeeded]; NSLog(@"Red View frame: %@", NSStringFromCGRect(redView.frame)); // outputs "Red View frame: {{0, 100}, {100, 100}}" } @end </code></pre> <h3>Additional Considerations</h3> <p>Slightly outside the scope of your question, here are some other issues you may run into, since I've worked on this exact problem in a real app:</p> <ul> <li>Calculating this in <code>heightForRowAtIndexPath:</code> might be expensive, so you may want to precalculate and cache the results</li> <li>Precalculation should be done on a background thread, but UIView layout doesn't work well unless it's done on the main thread</li> <li>You should definitely implement <code>estimatedHeightForRowAtIndexPath:</code> to reduce the impact of these performance issues</li> </ul> <h3>Using <code>intrinsicContentSize</code></h3> <p>In response to:</p> <blockquote> <p>Some subviews have stuff going on inside <code>layoutSubviews</code> so I can't call <code>systemLayoutForContentSize:</code></p> </blockquote> <p>You can use this method if you implement <code>intrinsicContentSize</code>, which lets a view suggest an optimal size for itself. One implementation for this might be:</p> <pre><code>- (CGSize) intrinsicContentSize { [self layoutSubviews]; return CGSizeMake(CGRectGetMaxX(self.bottomRightSubview.frame), CGRectGetMaxY(self.bottomRightSubview.frame)); } </code></pre> <p>This simple approach will only work if your <code>layoutSubviews</code> method doesn't refer to an already-set size (like <code>self.bounds</code> or <code>self.frame</code>). If it does, you may need to do something like:</p> <pre><code>- (CGSize) intrinsicContentSize { self.frame = CGRectMake(0, 0, 10000, 10000); while ([self viewIsWayTooLarge] == YES) { self.frame = CGRectInset(self.frame, 100, 100); [self layoutSubviews]; } return CGSizeMake(CGRectGetMaxX(self.bottomRightSubview.frame), CGRectGetMaxY(self.bottomRightSubview.frame)); } </code></pre> <p>Obviously, you'd need to adjust these values to match the particular layout of each view, and you may need to tune for performance.</p> <p>Finally, I'll add that due in part to the <a href="http://floriankugler.com/blog/2013/4/21/auto-layout-performance-on-ios">exponentially increasing cost of using auto-layout</a>, for all but the simplest table cells, I usually wind up using manual height calculation.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload