使用自动布局向 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,您的 UIScrollView
的 contentSize
由其子视图的 intrinsicContentSize
驱动,这里是您的 SinglePostView
。
问题是,SinglePostView
是UIView
的子类,它的intrinsicContentSize
在单独考虑时总是CGSizeZero
。您需要做的是使您的SinglePostView
的intrinsicContentSize
依赖于其子视图的intrinsicContentSize
。
那么,因为您的SinglePostView
的子视图是UILabels
,并且因为UILabel
的intrinsicContentSize
是显示其内容所需的最小尺寸,所以您的SinglePostView
的intrinsicContentSize
将等于其子视图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
推送scrollView
的contentSize
要清楚这一步,您必须了解在UIScrollView
和它的subviews
之间设置的每个约束实际上都会改变UIScrollView
的contentSize
,而不是它的实际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
正确显示其内容并且仍然可读,您可能希望它们是多行的并且滚动是垂直的,而不是水平的。这就是为什么您将SinglePostView
的width
设置为与scrollView
相同的原因。这样,您可以防止您的 scrollView
的 contentSize.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 上同时使用 UIStoryboard
和 Visual 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
的帧由scrollview
和scrollview
的superview
之间的约束决定。
contentSize
的 scrollview
由 scrollview
和 subview
的 scrollview
之间的约束决定。
您应该设置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,并为其子视图添加了自动布局,但它不滚动