Auto layout UIScrollView with subviews with dynamic heights

后端 未结 6 1156
情书的邮戳
情书的邮戳 2020-11-29 19:28

I\'m having troubles with UIScrollView using auto layout constraints. I have the following view hierarchy, with constraints set through IB:

- ScrollView (lea         


        
6条回答
  •  独厮守ぢ
    2020-11-29 19:45

    This is an example of how I have laid out a pure autolayout UIScrollView with a container view. I've commented to make it clearer:

    container is a standard UIView and body is a UITextView

    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        //add scrollview
        [self.view addSubview:self.scrollView];
    
        //add container view
        [self.scrollView addSubview:self.container];
    
        //body as subview of container (body size is undetermined)
        [self.container addSubview:self.body];
    
        NSDictionary *views = @{@"scrollView" : self.scrollView, @"container" : self.container, @"body" : self.body};
        NSDictionary *metrics = @{@"margin" : @(100)};
    
        //constrain scrollview to superview, pin all edges
        [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|" options:kNilOptions metrics:metrics views:views]];
        [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:kNilOptions metrics:metrics views:views]];
    
        //pin all edges of the container view to the scrollview (i've given it a horizonal margin as well for my purposes)
        [self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[container]|" options:kNilOptions metrics:metrics views:views]];
        [self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-margin-[container]-margin-|" options:kNilOptions metrics:metrics views:views]];
    
        //the container view must have a defined width OR height, here i am constraining it to the frame size of the scrollview, not its bounds
        //the calculation for constant is so that it's the width of the scrollview minus the margin * 2
        [self.scrollView addConstraint:[NSLayoutConstraint constraintWithItem:self.container attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.scrollView attribute:NSLayoutAttributeWidth multiplier:1.0f constant:-([metrics[@"margin"] floatValue] * 2)]];
    
        //now as the body grows vertically it will force the container to grow because it's trailing edge is pinned to the container's bottom edge
        //it won't grow the width because the container's width is constrained to the scrollview's frame width
        [self.container addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[body]|" options:kNilOptions metrics:metrics views:views]];
        [self.container addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[body]|" options:kNilOptions metrics:metrics views:views]];
    }
    

    In my example 'body' is a UITextView, but it could be anything else. If you happen to be using a UITextView as well note that in order for it to grow vertically it must have a height constraint that gets set in viewDidLayoutSubviews. So add the following constraint in viewDidLoad and keep a reference to it:

    self.bodyHeightConstraint = [NSLayoutConstraint constraintWithItem:self.body attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:nil multiplier:1.0f constant:100.0f];
    [self.container addConstraint:self.bodyHeightConstraint];
    

    Then in viewDidLayoutSubviews calculate the height and update the constraint's constant:

    - (void)viewDidLayoutSubviews
    {
        [super viewDidLayoutSubviews];
    
        [self.bodyHeightConstraint setConstant:[self.body sizeThatFits:CGSizeMake(self.container.width, CGFLOAT_MAX)].height];
    
        [self.view layoutIfNeeded];
    }
    

    The second layout pass is needed to resize the UITextView.

提交回复
热议问题