自动布局UIScrollView以及具有动态高度的子视图

luk*_*kas 40 objective-c uiscrollview ios autolayout

使用自动布局约束我遇到了UIScrollView的麻烦.我有以下视图层次结构,通过IB设置约束:

- ScrollView (leading, trailing, bottom and top spaces to superview)
-- ContainerView (leading, trailing, bottom and top spaces to superview)
--- ViewA (full width, top of superview)
--- ViewB (full width, below ViewA)
--- Button (full width, below ViewB)
Run Code Online (Sandbox Code Playgroud)

ViewA和ViewB的初始高度为200点,但可以通过单击将其垂直扩展到400点的高度.ViewA和ViewB通过更新其高度约束(从200到400)进行扩展.这是相应的片段:

if(self.contentVisible) {
    heightConstraint.constant -= ContentHeight;
    // + additional View's internal constraints update to hide additional content 
    self.contentVisible = NO;
} else {
    heightConstraint.constant += ContentHeight;
    // + additional View's internal constraints update to show additional content
    self.contentVisible = YES;
}

[self.view setNeedsUpdateConstraints];
[UIView animateWithDuration:.25f animations:^{
    [self.view layoutIfNeeded];
}];
Run Code Online (Sandbox Code Playgroud)

我的问题是,如果两个视图都被扩展,我需要能够滚动查看整个内容,现在滚动不起作用.如何使用约束来更新滚动视图以反映ViewA和ViewB高度的变化?

到目前为止,我能想到的唯一解决方案是在动画后手动设置ContainerView的高度,这将是ViewA + ViewB + Button高度的总和.但我相信还有更好的解决方案吗?

谢谢

lor*_*ixx 58

我使用如下的纯结构

-view
  -scrollView
    -view A
    -view B
    -Button
Run Code Online (Sandbox Code Playgroud)

确保Button(最后一个视图)有一个约束(从底部到superview的垂直间距,即scrollview),在这种情况下,无论你的视图A和视图B有什么变化,scrollView的高度都会相应地改变.

我参考了这个伟大的在线图书网站.

只需阅读"创建滚动视图"部分,您应该有一个想法.

我有类似的问题,我正在创建一个详细信息视图和使用Interface Builder与Auto布局是一个非常适合任务!

祝好运!

(其他资源:

关于滚动视图自动布局的堆栈溢出讨论.

iOS 6有一个发行说明,讨论UIScrollView的Auto Layout支持.

关于滚动视图的免费在线iOS书籍说明.这实际上对我帮助很大!


Dou*_*oss 25

假设我们有这样的层次结构(Label1是ContentView的子视图; ContentView是ScrollView的子视图,ScrollView是viewcontroller视图的子视图):

ViewController's View ScrollView ContentView Label1 Label2 Label3

ScrollView以与viewcontroller视图正常的方式使用autolayout进行约束.

ContentView被固定在scrollview的顶部/左侧/右侧/底部.这意味着你有一些约束使ContentView的上/下/前/后边缘被约束为等于ScrollView上的相同边.这是一个关键:这些约束是针对ScrollView的contentSize,而不是它的框架大小,如viewcontroller的视图所示.因此,它不会告诉ContentView与显示的ScrollView框架具有相同的框架大小,而是告诉Scrollview ContentView是其内容,因此如果contentview大于ScrollView框架,那么您可以滚动,就像设置scrollView.contentSize更大而scrollView.frame使内容可滚动.

这是另一个关键:现在你必须在ContentView,Label1-3和Scrollview之外的任何其他东西之间有足够的约束,以便ContentView能够从这些约束中找出它的宽度和高度.

因此,例如,如果您需要垂直滚动的标签集,则设置约束以使ContentView宽度等于ViewController视图的宽度,该宽度负责宽度.为了处理高度,将Label1顶部固定到ContentView顶部,将Label2顶部固定到Label1底部,将Label3顶部固定到Label2底部,最后(并且重要的是)将Label3的底部固定到ContentView的底部.现在它有足够的信息来计算ContentView的高度.

我希望这给某人一个线索,因为我阅读了上面的帖子,仍然无法弄清楚如何正确地使ContentView的宽度和高度限制.我缺少的是将Label3的底部固定在ContentView的底部,否则ContentView如何知道它有多高(因为Label3将会浮动,并且没有约束告诉ContentView它的底部y位置在哪里).

  • 很好的答案,没有人提到“将 Label3 的底部固定到 ContentView 的底部”。救了我一命! (2认同)

jws*_*art 7

这是我如何使用容器视图布局纯自动布局UIScrollView的示例.我评论说要更清楚:

容器是标准的UIView,而body是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]];
}
Run Code Online (Sandbox Code Playgroud)

在我的例子中,'body'是一个UITextView,但它可能是其他任何东西.如果您正好使用UITextView,请注意,为了使其垂直增长,它必须具有在viewDidLayoutSubviews中设置的高度约束.所以在viewDidLoad中添加以下约束并保持对它的引用:

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];
Run Code Online (Sandbox Code Playgroud)

然后在viewDidLayoutSubviews中计算高度并更新约束的常量:

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];

    [self.bodyHeightConstraint setConstant:[self.body sizeThatFits:CGSizeMake(self.container.width, CGFLOAT_MAX)].height];

    [self.view layoutIfNeeded];
}
Run Code Online (Sandbox Code Playgroud)

需要第二个布局传递来调整UITextView的大小.