41

我在使用自动布局约束时遇到了 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)

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];
}];

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

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

谢谢

4

6 回答 6

58

我使用如下纯结构

-view
  -scrollView
    -view A
    -view B
    -Button

确保 Button( THE LAST view ) 有一个约束(从它的底部到超级视图的垂直间距,即滚动视图),在这种情况下,无论视图 A 和视图 B 发生什么变化,滚动视图的高度都会相应地改变.

我参考了这个很棒的在线图书网站

只需阅读“创建滚动视图”部分,您应该会有一个想法。

我有类似的问题,我正在创建一个详细视图,并且使用具有自动布局的 Interface Builder 非常适合这项任务!

祝你好运!

(其他资源:

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

iOS 6 有一个关于 UIScrollView 的自动布局支持的发行说明。

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

于 2013-06-01T01:10:49.900 回答
27

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

ViewController's View ScrollView ContentView Label1 Label2 Label3

ScrollView 以正常方式被自动布局约束到 viewcontroller 的视图。

ContentView 固定在顶部/左侧/右侧/底部到滚动视图。这意味着您有约束使 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 位置在哪里)。

于 2015-05-17T02:21:21.123 回答
7

这是我如何使用容器视图布置纯自动布局 UIScrollView 的示例。我已发表评论以使其更清楚:

container 是一个标准的 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]];
}

在我的示例中,“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];

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

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];

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

    [self.view layoutIfNeeded];
}

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

于 2014-02-20T11:18:30.597 回答
1

使用此代码。ScrollView setContentSize 应该在主线程中被称为异步。

迅速:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    DispatchQueue.main.async {
        var contentRect = CGRect.zero

        for view in self.scrollView.subviews {
           contentRect = contentRect.union(view.frame)
        }

        self.scrollView.contentSize = contentRect.size
    }
}

目标 C:

 - (void)viewDidLayoutSubviews {
     [super viewDidLayoutSubviews];

     dispatch_async(dispatch_get_main_queue(), ^ {
                        CGRect contentRect = CGRectZero;

                        for(UIView *view in scrollView.subviews)
                           contentRect = CGRectUnion(contentRect,view.frame);

                           scrollView.contentSize = contentRect.size;
                       });
}
于 2016-09-02T12:52:40.493 回答
0

滚动视图每时每刻都应该知道它的内容大小。内容大小是从滚动视图的子视图中推断出来的。将控制器属性映射到 xib 文件中描述子视图高度的约束非常方便。然后在代码(动画块)中,您可以更改这些约束属性的常量。如果您需要更改整个约束,请保留对它的引用,以便稍后在父容器中更新它。

于 2014-07-03T10:13:48.567 回答
0

我的滚动视图变体!Dynamic!高度:

1) 添加scroll view到您的UIView. 固定所有(顶部、底部、前导、尾迹)约束。

2) 添加UIViewScroll View. 固定所有(顶部、底部、前导、尾迹)约束。这将是你的Content view。您也可以重命名它。

3)控制拖动从Content viewScroll view-Equal width

4) 将内容添加到您的UIView. 设置所需的约束。和!在下面的项目中添加底部约束不是 Greater or equal>=)(就像大多数人所说的)但是 Equal!设置20为例如。

在我的情况下,我有UIImageView内容。我已将其连接height到代码。如果我将其更改为 like 1000,则滚动可见。一切正常。

对我来说就像一个魅力。任何问题 - 欢迎评论。

于 2017-07-01T18:37:50.057 回答