Scrollview如何与Autolayout一起使用,以及为什么设置底部垂直空间约束使其工作?

时间:2013-03-23 03:39:05

标签: ios xcode uiscrollview autolayout

我试图了解UIScrollView在自动布局环境中的工作原理。到目前为止,我已经尝试阅读Apple文档,Stack Overflow研究,Google研究以及研究Matt Neuberg的工作示例。

Apple 6.1文档说:

  

UIScrollView对象的中心概念(或简称为滚动   视图)是一个视图,其原点可以在内容上调整   视图。它将内容剪辑到其框架,通常(但不是   必然)与应用程序的主窗口一致。一个   滚动视图跟踪手指的移动并调整原点   因此。正在通过滚动显示其内容的视图   视图根据新的原点绘制自己的那部分   固定到内容视图中的偏移量。滚动视图本身就是   没有绘图除了显示垂直和水平滚动   指标。滚动视图必须知道内容视图的大小   它知道何时停止滚动;默认情况下,它会“反弹”   滚动超出了内容的范围。

基于此,让我们来看看应该如何设置约束。

为了便于讨论,假设我们有3个视图作为一般情况 - 默认主视图控制器视图(A),其子视图是UIScrollview(B),UIScrollview有一个子视图,它是一个UIView(C)。让我们说我们希望(C)高达1000个单位。

因此,我们进入界面构建器,在故事板中选择视图控制器,并在属性检查器选项卡上将大小更改为自由形式。对于视图(A),(B)和(C),我们在尺寸检查器选项卡上将高度更改为1000.

(A)与主窗口之间的约束

设置约束的时间。文档明确指出"(Scrollview)将内容剪辑到其框架,该框架通常(...)与应用程序主窗口的内容一致(#34;)。在我们的示例中,(A)将与应用程序主窗口重合,并且不需要对此进行约束。

(A)和(B)之间的约束

现在文档清楚地知道(B)与(A)完全一致,所以我们在它们之间设置4个约束,引导空间,尾随空间,顶部空间和底部空间以超级查看所有常数为0.

(B)和(C)之间的约束

这里的文档不是那么简单。它说(B)的原点可以在(C)上调整,所以(B)肯定应该小于(C)。由于我们知道滚动只是上下滚动,我们可以将(B)和(C)之间的左右边界限制为零,我们总是希望它在中间,所以我们要添加中心x对齐。我们在它们之间添加3个约束,将空间和尾随空间引导到superview,其常量为0,中心x对齐。为了定位视图,我们需要顶部和底部的东西,老实说,我不确定应该如何根据文档设置这些约束。在模仿Matt Neuberg的example的基础上,我将它们作为顶级空间进行超级视图,使用常量零和底部空间来监视默认情况下生成的任何常量。这个超视距约束的底部空间是特殊的(显然),从现在起它将被称为" specialConstraint"。

那么......什么?!我们通过说(B)肯定小于(C)来开始这一段,并通过设置约束使它们完全相同的大小来结束它。 问题1 - 为什么会这样?

自身对(C)的限制

我们知道(C)必须大于(B),以便(B)有一些可以滚动的东西,(C)应该根据自己的约束来确定它的大小。很容易,我们设置了1个约束,height = 1000。

specialConstraint

现在我们在视图控制器中为specialConstraint创建一个插座,在viewDidLoad方法中我们设置self.specialConstraint.constant = 0;这意味着内容视图的底部应该精确地固定在滚动视图的底部,这样就不会向下滚动。但这在Matt Neuberg的例子中有效。 问题2 - 为什么会这样?

Scrollview滚动时真正发生的事情

我认为合乎逻辑的做法是将滚动视图的框架固定,内容视图的原点(或内容偏移)随滚动而移动,内容显示滚动视图就像一个窗口。类似于通过滑动纸张从墙上的孔中查看纸张。

然而,根据我的阅读,滚动视图的框架实际上正在移动,就像固定页面上的放大镜一样。

问题3 - 有人可以解释滚动视图滚动时发生了什么,对象的帧起源实际发生了变化,为什么这样做?

问题4 - 任何人都可以用简单的英语解释如何在(A),(B)和(C)之间设置约束吗?

3 个答案:

答案 0 :(得分:0)

工作示例

您好。首先,这里是几天前写的工作代码滚动视图示例。

[self addSubview:scrollView];

id views = NSDictionaryOfVariableBindings(scrollView);
[self addConstraints:PVVFL(@"H:|[scrollView]|").withViews(views).asArray];
[self addConstraints:PVVFL(@"V:|[scrollView]|").withViews(views).asArray];

约束代码在Parus lib。

的帮助下写入

正如您所说,滚动视图与其超级视图相关联。换句话说,它完全填满了。

早些时候我设置了一个scrollView树。

id views = NSDictionaryOfVariableBindings(photoView, storeNameLabel, selectStoreButton, tagsLabel, tagsContainer, editTagsButton, commentView, sendButton, cancelButton);
[scrollView addConstraints:PVVFL(@"V:|-15-[photoView(150)]").withViews(views).asArray];
[scrollView addConstraints:PVVFL(@"|-15-[photoView]-15-|").withViews(views).asArray];
[scrollView addConstraint:PVCenterXOf(photoView).equalTo.centerXOf(scrollView).asConstraint];

[scrollView addConstraint:PVTopOf(storeNameLabel).equalTo.bottomOf(photoView).plus(20).asConstraint];
[scrollView addConstraints:
 PVVFL(@"|-15-[storeNameLabel]-(>=15)-[selectStoreButton]-15-|")
 .alignAllBaseline.withViews(views).asArray];
[selectStoreButton setContentCompressionResistancePriority:UILayoutPriorityRequired
                                                   forAxis:UILayoutConstraintAxisHorizontal];

[scrollView addConstraints:
 PVVFL(@"V:[storeNameLabel]-15-[tagsLabel][tagsContainer]").alignAllLeft.withViews(views).asArray];
[scrollView addConstraint:PVRightOf(tagsContainer).equalTo.rightOf(selectStoreButton).asConstraint];
[scrollView addConstraint:PVTopOf(editTagsButton).equalTo.bottomOf(tagsContainer).plus(10).asConstraint];
[scrollView addConstraint:PVWidthOf(editTagsButton).equalTo.widthOf(tagsContainer).asConstraint];
[scrollView addConstraint:PVLeftOf(editTagsButton).equalTo.leftOf(tagsContainer).asConstraint];

[scrollView addConstraint:PVTopOf(commentView).equalTo.bottomOf(editTagsButton).plus(10).asConstraint];
[scrollView addConstraint:PVWidthOf(commentView).equalTo.widthOf(editTagsButton).asConstraint];
[scrollView addConstraint:PVLeftOf(commentView).equalTo.leftOf(editTagsButton).asConstraint];
[scrollView addConstraint:PVHeightOf(commentView).moreThan.constant(100).asConstraint];
[scrollView addConstraint:PVLeftOf(cancelButton).equalTo.leftOf(commentView).asConstraint];
[scrollView addConstraints:PVVFL(@"[cancelButton]-(>=15)-[sendButton(==cancelButton)]").alignAllBaseline.withViews(views).asArray];
[scrollView addConstraint:PVRightOf(sendButton).equalTo.rightOf(commentView).asConstraint];
[scrollView addConstraints:PVVFL(@"V:[commentView]-10-[cancelButton]-10-|").withViews(views).asArray];

你怎么看,这是非常复杂的布局。 但关键点:内部观点具有固定的高度,由其内在的内容大小提供。他们一起试图增加scrollView的高度。 scrollView身高由self.view确定 !!!约束求解器必须生成错误。但是,

理论

对于这种情况,Apple有特殊Tech note。还有一些有趣的时刻:

  

要使此功能适用于自动布局,滚动视图中的顶部,左侧,底部和右侧边缘现在表示其内容视图的边缘

此注释还提供了使用滚动视图和自动布局时不同方法的一些示例。

现在我将尝试回答你的问题。

答案

  

问题1 - 为什么会这样?

正如您在上面所读到的,这是因为滚动视图的自动布局设置内容视图大小而不是帧大小。

  

问题2 - 为什么会这样?

同样,事情是内容大小。 特殊约束方法看起来像是界面构建器上的hack。在xib你有正常的大小视图,并且在nib文件实例化之后你只需将事情恢复正常,将此约束修复为0,这意味着滚动视图的内容大小将等于C视图。 / p>

  

问题3 - 有人可以解释滚动视图滚动时发生了什么,对象的帧起源实际发生了变化,为什么这样做?

您可以在本文中找到有关此过程的详细说明:Understanding Scroll Views,但简短的回答是: UIScroll视图通过更改self.bounds.origin点来执行滚动。

  

问题4 - 任何人都可以用简单的英语解释如何在(A),(B)和(C)之间设置约束吗?

您应该通过将所有边缘固定到内部视图来在滚动视图中设置约束。换句话说,滚动视图的所有边都必须固定两次:通过内部视图和superview。

如果对这个问题的回答尚不清楚,你可以从一开始就阅读这个答案。

答案 1 :(得分:0)

我找到了很多例子,但没有解释如何在运行时设置contensize。所以用一个按钮。

实际上,这一切都归结为你的scrollviews内容和scrollView之间的关系。

现在,您可以在故事板中滚动滚动视图并调整大小而无需代码。只有你必须正确行事。

要使用按钮调整contensize(在运行时),您需要调整内容的约束。

这很难理解。我已将代码放在github http://goo.gl/qrXANp上,你可以在这里找到关于它的视频...... https://www.youtube.com/watch?v=4oCWxHLBQ-A

答案 2 :(得分:0)

  

解决这个神秘问题的基本技巧是..嵌入另一个视图   在scrollview内部并设置下面的约束

     

UIView - > UIScrollView - > ContentView

你可以在运行时继续向contentView添加子视图,uiscrollview内容大小会自动增加

   self.ContentView = [[UIView alloc] initWithFrame:CGRectZero];
    self.ContentView.translatesAutoresizingMaskIntoConstraints = NO;
    self.scrollview = [[UIScrollView alloc] initWithFrame:CGRectZero];
    self.scrollview.translatesAutoresizingMaskIntoConstraints = NO;
    [self.scrollview sizeToFit];

    [self.scrollview addSubview:self.ContentView];
    [self.view addSubview:self.scrollview];

    NSMutableDictionary *views = [[NSMutableDictionary alloc] init];

    [views setValue:self.ContentView forKey:@"ContentView"];
    [views setValue:self.scrollview forKey:@"scrollview"];
    [views setValue:self.view forKey:@"mainView"];

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

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

    [self.scrollview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[ContentView]|"
                                                                             options:0
                                                                             metrics:nil
                                                                               views:views]];

    [self.scrollview addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[ContentView]|"
                                                                             options:0
                                                                             metrics:nil
                                                                               views:views]];

    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[ContentView(==mainView)]"
                                                                      options:0
                                                                      metrics:nil
                                                                        views:views]];

    self.view.contentMode = UIViewContentModeRedraw;

    [self.view setNeedsUpdateConstraints];