Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIView not obeying autolayout constraints in UIScrollView

I am adding a UIView to a UIScrollView and constraining it such that it fills the horizontal space, except for some margins. My visual constraint looks like this:

@"|-16-[theLineView]-16-|"

I have made the view one pixel high so it will appear as a line, and placed it between two text labels:

@"V:[someOtherStuff]-[aTextLabel]-[theLineView]-[anotherLabel]"

However, I am finding that the width of the line is only expanding as far as the width of the longest label above/below it.

Why would this be?

P.S I have read this http://developer.apple.com/library/ios/#technotes/tn2154/_index.html

enter image description here

Code

Here is the entirety of the view controller code from a test project that exhibits this issue on the iPad sim.

- (void)viewDidLoad

{ [super viewDidLoad];

self.scrollView = [[UIScrollView alloc] init];
self.scrollView.translatesAutoresizingMaskIntoConstraints = NO;
self.scrollView.backgroundColor = [UIColor greenColor];
[self.view addSubview:self.scrollView];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|[scrollView]|"
                                                                 options:0
                                                                 metrics:0
                                                                   views:@{@"scrollView":self.scrollView}]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|"
                                                                  options:0
                                                                  metrics:0
                                                                    views:@{@"scrollView":self.scrollView}]];

self.line1 = [[UIView alloc] init];
self.line2 = [[UIView alloc] init];
self.label1 = [[UILabel alloc] init];
self.label2 = [[UILabel alloc] init];
self.label3 = [[UILabel alloc] init];

for (UILabel *label in @[self.label1, self.label2, self.label3])
{
    label.text = @"I am a label and I am long enough that I can be multiline on an iphone but single on ipad";
}

for (UIView *view in @[self.line1, self.line2, self.label1, self.label2, self.label3])
{
    view.translatesAutoresizingMaskIntoConstraints = NO;
    view.backgroundColor = [UIColor redColor];
    [self.scrollView addSubview:view];
}

//horizontal layout - all views/labels should fill the horizontal space expect for margin
for (UIView *view in  @[self.line1, self.line2, self.label1, self.label2, self.label3])
{
    NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|-16-[view]-16-|"
                                                                   options:0
                                                                   metrics:0
                                                                     views:@{@"view":view}];
    [self.scrollView addConstraints:constraints];
}

//vertical layout - stack em up
[self.scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[lab1]-[line1(==1)]-[lab2]-[line2(==1)]-[lab3]-|"
                                                                  options:0
                                                                  metrics:0
                                                                    views:@{@"lab1":self.label1, @"line1":self.line1, @"lab2":self.label2, @"line2":self.line2, @"lab3":self.label3}]];

}

like image 951
Ben Packard Avatar asked Jul 21 '13 13:07

Ben Packard


Video Answer


1 Answers

UIScrollView automatically shrinks to fit the views inside it. You need to set the width absolutely somewhere.

Recommended tactic:

  1. Completely fixiate the scrollview inside its parent-view using constraints (leading, trailing, top, bottom).
  2. Create a UIView inside the UIScrollView and put everything you need inside it.
  3. Set the constraints so that the UIView will act as a content-view (this means it is big enough to include all elements). Use intrinsic content-size, shrink-resistance and chaining of elements with constraints a lot. The resulting layout must be well-defined and unique (this means if you were to remove all constraints to the outside, the layout would still work).
  4. Connect the bounds of the UIView with their superview (which is the actual content-view of the UIScrollView, NOT the UIScrollView!).

If you do this in interface-builder (it is possible), you need to re-check your constraints every time you touch something in that scene. And by touch I mean "select" not only "modify".

like image 50
patric.schenke Avatar answered Sep 16 '22 13:09

patric.schenke