UIView with Auto layout, get his size

19,350

Solution 1

sizeToFit is the wrong API to use if you're using autolayout.

The correct api to use for autolayout is UIView systemLayoutSizeFittingSize:, most likely passing UILayoutFittingCompressedSize. This will return a CGSize which is the minimum size that can bound the view with its specified constraints.

Check this link

EDIT: In response to the additional code that was posted...

I've never used the Masonry library before but it's neat!

UILabels provide an intrinsicContentSize that is large enough to display their text. In order to calculate this size for multi-line text the label needs to know a single dimension that will be fixed, and that is the desired-width dimension via the preferredMaxLayoutWidth property. If you don't provide a preferredMaxLayoutWidth value (defaults to 0), the label calculates an intrinsic content size AS IF it were laying out a single line of text.

In your code you never set the label's preferredMaxLayoutWidth so any layout performed by autolayout that relies on the intrinsicContentSize of the label will not be what you're expecting.

I took your code and distilled it into a new example, which you should be able to copy/paste into a clean viewcontroller subclass:

#import "Masonry.h"

@interface TSViewController ()
@end

@implementation TSViewController
{
    UIScrollView*   _scrollView;
    UIView*         _containerView;

    UILabel*        _titleLabel;
    UILabel*        _contentLabel;
}

- (void) viewDidLoad
{
    [super viewDidLoad];

    self.view.backgroundColor = [UIColor darkGrayColor];

    // create/configure our scrollview
    _scrollView = [UIScrollView new];
    _scrollView.translatesAutoresizingMaskIntoConstraints = NO;
    _scrollView.backgroundColor = [UIColor scrollViewTexturedBackgroundColor];
    [self.view addSubview: _scrollView];

    // create/configure our container
    _containerView = [UIView new];
    _containerView.translatesAutoresizingMaskIntoConstraints = NO;
    _containerView.backgroundColor = [UIColor yellowColor];
    [_scrollView addSubview: _containerView];

    // create/configure our content - title and content labels
    _titleLabel = [UILabel new];
    _titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
    _titleLabel.numberOfLines = 1;
    _titleLabel.font = [UIFont fontWithName: @"Courier" size: 80];
    _titleLabel.text = @"Lorem Ipsum";
    _titleLabel.backgroundColor = [UIColor lightGrayColor];
    _titleLabel.textAlignment = NSTextAlignmentCenter;
    [_containerView addSubview: _titleLabel];

    _contentLabel = [UILabel new];
    _contentLabel.translatesAutoresizingMaskIntoConstraints = NO;
    _contentLabel.numberOfLines = 0;
    _contentLabel.font = [UIFont fontWithName: @"Courier" size: 40];
    _contentLabel.text = self.loremIpsum;
    [_containerView addSubview: _contentLabel];

    // configure constraints for each view:

    [_scrollView mas_makeConstraints: ^(MASConstraintMaker *make) {

        // glue the scrollview to its superview with a 20 point inset:

        make.top.equalTo( self.view.mas_top ).with.offset( 20 );
        make.left.equalTo( self.view.mas_left ).with.offset( 20 );
        make.right.equalTo( self.view.mas_right ).with.offset( -20 );
        make.bottom.equalTo( self.view.mas_bottom ).with.offset( -20 );
    }];

    [_containerView mas_makeConstraints: ^(MASConstraintMaker *make) {

        // per the iOS 6.0 Release Notes, this is how to use auto-layout for a container view
        // inside a scrollview.  basically, tie the edges of the containerview to the scrollview.
        // http://developer.apple.com/library/ios/#releasenotes/General/RN-iOSSDK-6_0/
        // see section "Here are some notes regarding Auto Layout support for UIScrollView"

        make.top.equalTo( _scrollView.mas_top );
        make.left.equalTo( _scrollView.mas_left );
        make.right.equalTo( _scrollView.mas_right );
        make.bottom.equalTo( _scrollView.mas_bottom );

        // match the width of the containerview to the scrollview width:
        make.width.equalTo( _scrollView.mas_width );

        // match the height of the containerview to the intrinsic height of the contentlabel + 100 points for our fixed height title

        // (this is the magical part.)
        make.height.equalTo( _contentLabel.mas_height ).with.offset( 100 );
    }];

    [_titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {

        // tie the contentlabel edges to the sides of its container:
        make.top.equalTo( _containerView.mas_top ).with.offset(0);
        make.left.equalTo( _containerView.mas_left ).with.offset(0);
        make.right.equalTo( _containerView.mas_right ).with.offset(0);

        // fixed height
        make.height.equalTo( @100 );
    }];

    [_contentLabel mas_makeConstraints:^(MASConstraintMaker *make) {

        // tie top edge to the bottom of our title
        make.top.equalTo( _titleLabel.mas_bottom );

        // tie the remaining edges to the sides of the container:
        make.left.equalTo( _containerView.mas_left );
        make.right.equalTo( _containerView.mas_right );
        make.bottom.equalTo( _containerView.mas_bottom );
    }];
}

- (void) viewWillLayoutSubviews
{
    // perform a scrollview layout so the containerView width will be set
    [_scrollView setNeedsLayout];
    [_scrollView layoutIfNeeded];

    // update the preferred layout width of the content label;
    // this affects the label's intrinsicContentSize and will force our constraints to be recalculated
    CGFloat preferredWidth = _containerView.frame.size.width;
    _contentLabel.preferredMaxLayoutWidth = preferredWidth;


    // we don't need this since the scrollview is fully using autolayout to adjust the content-size.  but just to show that it works:

    CGSize fittingSize = [_containerView systemLayoutSizeFittingSize: UILayoutFittingCompressedSize];

    NSLog( @"container fitting size: %@", NSStringFromCGSize( fittingSize ));
}

- (NSString*) loremIpsum
{
    return @"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing <br>Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type<br> and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.";
}

@end

And, here's the output from systemLayoutSizeFittingSize when the constraints are all working nicely:

2013-08-05 15:01:16.309 testLabelConstraints[8265:c07] container fitting size: {721, 6868}

Solution 2

If I understand you, the problem is getting view or views that dynamically size to scroll properly when they are subviews of a ScrollView. It this is it, I've had this problem and it can be solved without even having to worry about content size.

Here is a link to a tech note from Apple entitled "UIScrollView and Autolayout" here

http://developer.apple.com/library/ios/technotes/tn2154/_index.html

There is easy to follow source in the note which detail two approaches to solving this issue. Hope this helps, it really helped me.

Additionally, if you're trying to iterate through a collection of subviews inside your ScrollView, read this article and example, again, lucid explanation with code.

Apeth Programming IOS 6, Chapter 20. Scroll Views on line here:

http://www.apeth.com/iOSBook/ch20.html

Share:
19,350
dormitkon
Author by

dormitkon

Updated on June 07, 2022

Comments

  • dormitkon
    dormitkon almost 2 years

    I have one UIView witch is populated with content programmatically. Layout in this UIView is made using Auto-layout.

    This view needs to be subview of one UIScrollView, and it needs to scroll vertically.

    I have code like this:

    // self.scrollview is defined in IB and it has constraints to edges (margin:0 0 0 0)
    
    UIView *contentView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 1024, MAXFLOAT)];
    [contentView setTranslatesAutoresizingMaskIntoConstraints:YES];
    
    // Then I call some my parser which add subviews to the contentView and layout them using constraints.
    
    [self.scrollview addSubview:contentView];
    [contentView setNeedsLayout];
    [contentView layoutIfNeeded];
    
    // Now, I need to know height of the contentView to be able to set contentSize to self.scrollview
    

    // UPDATE

    Ok, I have this UILabel, added to self.content which is UIView created with: [[UIView alloc] initWithFrame:CGRectMake(self.layoutMargin, self.layoutMargin, 1024 - self.layoutMargin*2, 100)]);

    This self.content view is just container for me, which needs to be placed in some UIScrollView. I need the height of the self.content to set contentSize of the UIScrollView.

    [self.content setTranslatesAutoresizingMaskIntoConstraints:YES];
    
    [self.content setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
    [self.content setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
    
    UILabel *textlabel = [[UILabel alloc] init];
    UIFont *font;
    
    
    font = [UIFont fontWithName:@"InterstatePlus-Regular" size:16];
    
    UIFont *bold;
    
    bold = [UIFont fontWithName:@"InterstatePlus-Bold" size:16];
    NSDictionary *style = @{
                            @"$default" : @{NSFontAttributeName  : font},
                            @"b"        : @{NSFontAttributeName  : bold},
                            @"em"       : @{NSFontAttributeName  : [UIFont fontWithName:@"HelveticaNeue-Italic" size:14]},
                            @"h1"       : @{NSFontAttributeName  : [UIFont fontWithName:@"HelveticaNeue-Medium" size:48]},
                            @"h2"       : @{NSFontAttributeName  : [UIFont fontWithName:@"HelveticaNeue-Medium" size:36]},
                            @"h3"       : @{NSFontAttributeName  : [UIFont fontWithName:@"HelveticaNeue-Medium" size:32]},
                            @"h4"       : @{NSFontAttributeName  : [UIFont fontWithName:@"HelveticaNeue-Medium" size:24]},
                            @"h5"       : @{NSFontAttributeName  : [UIFont fontWithName:@"HelveticaNeue-Medium" size:18]},
                            @"h6"       : @{NSFontAttributeName  : [UIFont fontWithName:@"HelveticaNeue-Medium" size:16]}
                            };
    [textlabel setNumberOfLines:0];
    
    
    
    NSError *error = nil;
    NSString *string = @"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing <br>Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type<br> and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.";
    NSString *replacedString = [string stringByReplacingOccurrencesOfString:@"<br>" withString:@"\n"];
    NSAttributedString *attributedString = [SLSMarkupParser attributedStringWithMarkup:replacedString style:style error:&error];
    NSMutableAttributedString *mutAttributedString = [[NSMutableAttributedString alloc] initWithAttributedString:attributedString];
    
    NSInteger strLength = [attributedString length];
    NSMutableParagraphStyle *paragraphstyle = [[NSMutableParagraphStyle alloc] init];
    [mutAttributedString addAttribute:NSParagraphStyleAttributeName
                                value:paragraphstyle
                                range:NSMakeRange(0, strLength)];
    
    if (mutAttributedString) {
        textlabel.attributedText = mutAttributedString;
    }
    
    
    [textlabel setTranslatesAutoresizingMaskIntoConstraints:NO];
    
    [textlabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
    [textlabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
    
    
    textlabel.textAlignment = NSTextAlignmentLeft;
    
    [textlabel setTextColor:[UIColor blackColor]];
    
    [textlabel setBackgroundColor:[UIColor clearColor]];
    textlabel.lineBreakMode = NSLineBreakByWordWrapping;
    [textlabel setNumberOfLines:0];
    [self.content addSubview:textlabel];
    
    [textlabel mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.content.mas_top).with.offset(0); 
        make.right.equalTo(self.content.mas_right).with.offset(0);
        make.height.equalTo(@1200);
        make.left.equalTo(self.content.mas_left).with.offset(0);
    }];
    [self.containerView addSubview:self.content];
    [self.containerView setBackgroundColor:[UIColor redColor]];
    [self.content setBackgroundColor:[UIColor yellowColor]];
    
    CGSize s = [self.content systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
    NSLog(@"Size: %f %f", s.width, s.height);
    

    This is Log: Size: 17713.000000 0.000000

    And it looks like: http://d.pr/i/Rxku

  • dormitkon
    dormitkon almost 11 years
    U used: CGSize s = [self.mainContentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize]; NSLog(@"Size: %f %f", s.width, s.height); And I'm getting: Size: 5584.000000 0.000000 :(
  • TomSwift
    TomSwift almost 11 years
    @dormitkon -Then your constraints are likely mixed up. Can you try a simpler case and add back in the complexity once you have it working? Or post more code with your constraints showing so we can help.
  • dormitkon
    dormitkon almost 11 years
    Constraints are generated parsed from some XML layout, so they can change from page to page. I checked with autolayouttrace and layout is fine. :|
  • TomSwift
    TomSwift almost 11 years
    The constraints may be "fine" but they may be permissive of a zero-height container (as you are seeing). Meaning that the constraints can produce a "correct" layout for any container view with zero height.
  • dormitkon
    dormitkon over 10 years
    Maybe the problem is container view? This view in subview of UIScrollView (where I use frames, because I can't use autolayout into UISCrollView).
  • dormitkon
    dormitkon over 10 years
    Tried without uiscrollview, and the same story.
  • TomSwift
    TomSwift over 10 years
    I really suggest you start with a basic setup (a UIView with a UIView subview, and some basic constraints) to get systemLayoutSizeFittingSize to work. Once you have it working, and you understand why (or why / when it doesn't), then add in more complexity. Iterate until you have a good proxy for the view hierarchy/constraints in your real app.
  • Antoine
    Antoine almost 10 years
    Setting preferredMaxLayoutWidth did the trick for me as well, thanks!
  • Dean Kelly
    Dean Kelly over 7 years
    It might be important to mention using the method where you specify horizontal and vertical priorities. It can have a pretty big impact on the resulting size.