使用自动布局向 UIScrollView 添加动态大小的视图(高度)

Posted

技术标签:

【中文标题】使用自动布局向 UIScrollView 添加动态大小的视图(高度)【英文标题】:Adding a dynamically sized view (height) to a UIScrollView with Auto Layout 【发布时间】:2014-07-24 08:20:32 【问题描述】:

这是我对此详细视图的视图结构(博客应用程序,我想查看在滚动视图内具有动态高度的整个帖子):

UIView
  -UIScrollView
    -SinglePostView (custom view)
      -Title
      -Subtitle
      -Content

通常要向 UIView 添加“单个帖子视图”,我只需将其实例化,将其提供给我的 Post 对象,然后添加单个约束,将 singlePostView 的宽度固定到超级视图,此时事情会很好地布置.但是,当我尝试将其添加到滚动视图时,它既不显示也不滚动。

UIScrollView *scrollView = [[UIScrollView alloc] init];
[self.view addSubview:scrollView];

NOBSinglePostView *singlePost = [[NOBSinglePostView alloc] initWithPost:self.post];
[scrollView addSubview:singlePost];
singlePost.translatesAutoresizingMaskIntoConstraints = NO;
scrollView.translatesAutoresizingMaskIntoConstraints  = NO;

NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(scrollView,singlePost);
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:0 metrics: 0 views:viewsDictionary]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|" options:0 metrics: 0 views:viewsDictionary]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[singlePost]|" options:0 metrics: 0 views:viewsDictionary]];

【问题讨论】:

【参考方案1】:

在您呈现的结构中,使用 AutoLayout,您的 UIScrollViewcontentSize 由其子视图的 intrinsicContentSize 驱动,这里是您的 SinglePostView

问题是,SinglePostViewUIView 的子类,它的intrinsicContentSize 在单独考虑时总是CGSizeZero。您需要做的是使您的SinglePostViewintrinsicContentSize 依赖于其子视图的intrinsicContentSize

那么,因为您的SinglePostView 的子视图是UILabels,并且因为UILabelintrinsicContentSize 是显示其内容所需的最小尺寸,所以您的SinglePostViewintrinsicContentSize 将等于其子视图intrinsicContentSizes 的总和,即显示所有三个标签的内容所需的总大小。

这里是怎么做的。

第 1 步:删除所有自动设置的约束

首先,正如您部分所做的那样,您需要删除系统自动设置的所有约束。

假设您没有在情节提要或 XIB 中设置任何约束(或者您甚至没有这些约束),只需执行以下操作:

scrollView.translatesAutoresizingMaskIntoConstraints  = NO;
singlePost.translatesAutoresizingMaskIntoConstraints = NO;
titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
subtitleLabel.translatesAutoresizingMaskIntoConstraints = NO;
contentLabel.translatesAutoresizingMaskIntoConstraints = NO;

现在您有了清晰的计划,您可以开始设置自己的约束条件了。

第 2 步:约束scrollView

首先,让我们像您一样创建视图引用字典以供 AutoLayout 使用:

NSDictionary *views = NSDictionaryOfVariableBindings(scrollView, singlePost, titleLabel, subtitleLabel, contentLabel);

然后,就像你已经做过的那样,让我们​​将滚动视图限制为其父视图的大小:

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[scrollView]-0-|" options:0 metrics:0 views:views]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[scrollView]-0-|" options:0 metrics:0 views:views]];

第 3 步:约束SinglePostView 推送scrollViewcontentSize

要清楚这一步,您必须了解在UIScrollView 和它的subviews 之间设置的每个约束实际上都会改变UIScrollViewcontentSize,而不是它的实际bounds。例如,如果您将UIImageView 约束到其父UIScrollView 的边界,然后将两倍于UIScrollView 大小的图像放入UIImageView,您的图像不会缩小,它是@987654355 @ 将采用其图像的大小并在 UIScrollView 内变为可滚动的。

所以你必须在这里设置:

[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[singlePost]-0-|" options:0 metrics:0 views:views]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[singlePost]-0-|" options:0 metrics:0 views:views]];
[scrollView addConstraint:[NSLayoutConstraint constraintWithItem:singlePost
                                                         attribute:NSLayoutAttributeWidth
                                                         relatedBy:NSLayoutRelationEqual
                                                            toItem:scrollView
                                                         attribute:NSLayoutAttributeWidth
                                                        multiplier:1.0f
                                                          constant:0.0f]];

前两个约束非常明显。然而,第三个在这里是因为,为了让您的 UILabels 正确显示其内容并且仍然可读,您可能希望它们是多行的并且滚动是垂直的,而不是水平的。这就是为什么您将SinglePostViewwidth 设置为与scrollView 相同的原因。这样,您可以防止您的 scrollViewcontentSize.width 超出其 bounds.width

第 4 步:约束您的 UILabels 以“推动”您的 SinglePostView 的边界

第四步也是最后一步,您现在需要对SinglePostView 的子视图设置约束,以便从它们那里获得intrinsicContentSize

这是你的做法(最简单的实现,没有边距,一个标签一个接一个垂直):

[singlePost addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[titleLabel]-0-|" options:0 metrics:0 views:views]];
[singlePost addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[subtitleLabel]-0-|" options:0 metrics:0 views:views]];
[singlePost addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-0-[contentLabel]-0-|" options:0 metrics:0 views:views]];
[singlePost addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-0-[titleLabel]-0-[subtitleLabel]-[contentLabel]-0-|" options:0 metrics:0 views:views]];

你应该完成了。

最后一个建议,您绝对应该查看UIStoryboard 来做这些事情。它更简单,更直观。

希望这会有所帮助,

P.S.:如果你愿意,我可以花一些时间在 Github 上同时使用 UIStoryboardVisual Format Language 推送一个项目。只要告诉我你是否需要一个。

祝你好运。

【讨论】:

感谢您的精彩描述。第 3 步并确保 contentsize 的宽度有界是缺少的链接 @Msencenb 如果在 iPhone 4S 上 scrollView 的高度缩小(相同宽度)怎么办?我有同样的结构,它在 ScrollView 上有五个相同大小的 UIView。用户只允许水平滚动。它在 iPhone 5、iPhone 6、iPhone 6 plus 上完美运行。但是,当在 iPhone 4 上滚动视图的高度缩小(故意由于内容拥抱和内容抵抗)时,我的 UIView 不适合。我必须垂直向下滚动才能看到整张照片,但这不是我想要的。我检查了 scrollView 内容大小的高度,它和 iPhone 5 上的一样。 感谢为 uiscrollviews 引用intrinsicContentSize。我以为我失去了它,因为我有需要 instrinsicContentSize 覆盖的自定义 uiviews。【参考方案2】:

在自动布局中

scrollview 的帧由scrollviewscrollviewsuperview 之间的约束决定。 contentSizescrollviewscrollviewsubviewscrollview 之间的约束决定。

您应该设置singlePostView 的大小。 ScrollView 从中计算 contentSize。 (您需要明确添加大小约束)

CGFloat heightOfSinglePostView = calculate..
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[singlePost(heightOfSinglePostView)]|" options:0 metrics: 0 views:viewsDictionary]];

【讨论】:

如果我不知道 singlePostView 在屏幕上显示之前的高度怎么办?你是说我需要手动计算高度本身(使用 NSString + 图像高度方法)?这似乎与 Auto Layout 的用途完全相反 如果内容视图具有固有大小(如 UIImageView),则不需要。但如果不是,我认为需要计算内容大小。我将进行更多测试以找出更好的解决方案。如果你已经找到了,请告诉我

以上是关于使用自动布局向 UIScrollView 添加动态大小的视图(高度)的主要内容,如果未能解决你的问题,请参考以下文章

如何使用情节提要和自动布局在 UIScrollView 中添加动态高度/空间元素? [复制]

具有动态视图数量和自动布局 iOS6 的 UIScrollView

以编程方式布局 UIScrollView,并为其子视图添加了自动布局,但它不滚动

UIScrollView 不能使用自动布局和动态约束滚动

如何使 UIScrollView 与自动布局和动态内容一起使用? [复制]

UIScrollView 动态高度内容(自动布局)没有填满整个空间