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

标签 ios xcode uiscrollview autolayout

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

Apple 6.1 文档说:

The central notion of a UIScrollView object (or, simply, a scroll view) is that it is a view whose origin is adjustable over the content view. It clips the content to its frame, which generally (but not necessarily) coincides with that of the application’s main window. A scroll view tracks the movements of fingers and adjusts the origin accordingly. The view that is showing its content “through” the scroll view draws that portion of itself based on the new origin, which is pinned to an offset in the content view. The scroll view itself does no drawing except for displaying vertical and horizontal scroll indicators. The scroll view must know the size of the content view so it knows when to stop scrolling; by default, it “bounces” back when scrolling exceeds the bounds of the content.



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

为了便于讨论,假设我们有 3 个 View 作为一般情况 - 默认主 View Controller View (A),其 subview 是 UIScrollview (B),而 UIScrollview 有一个 subview ,它是 UIView (C )。假设我们希望 (C) 高 1000 个单位。

所以我们进入界面构建器,在 Storyboard中选择 View Controller ,并在属性检查器选项卡上将大小更改为自由格式。对于 View (A)、(B) 和 (C),我们将尺寸检查器选项卡上的高度更改为 1000。

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

是时候设置约束了。该文档明确指出“( ScrollView )将内容剪辑到其框架中,这通常(...)与应用程序主窗口的框架一致”。在我们的示例中 (A) 将与应用程序主窗口重合,因此不需要任何约束。

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

现在文档很清楚让 (B) 与 (A) 完全重合,因此我们将在它们之间设置 4 个约束,前导空间、尾随空间、顶部空间和底部空间以使用常量 0 来超 View 。

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

这里的文档不是那么简单。它说(B)的原点在(C)上是可调的,所以(B)肯定应该小于(C)。由于我们知道滚动只会向上和向下,我们可以将 (B) 和 (C) 之间的左右边缘限制为零,并且我们总是希望它在中间,因此我们将添加一个中心x 对齐。我们将在它们之间添加 3 个约束,前导空间和尾随空间以常量 0 和中心 x 对齐。为了定位 View ,我们需要一些东西用于顶部和底部,老实说,我不确定应该如何根据文档设置这些约束。基于模仿 Matt Neuberg 的 example我将它们的顶部空间设置为常量为零,并将底部空间设置为默认生成的任何常量。这个底部空间到 super View 约束是特殊的(显然),从现在开始它应该被称为“specialConstraint”。

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

(C) 本身的约束

我们知道 (C) 必须大于 (B) 以便 (B) 可以滚动,并且 (C) 应根据其自身的约束确定其大小。很简单,我们设置了 1 个约束,高度 = 1000。

特殊约束

现在我们在 View Controller 中为 specialConstraint 创建一个导出,并在 viewDidLoad 方法中设置 self.specialConstraint.constant = 0;这意味着内容 View 的底部应该准确地固定在 ScrollView 的底部,这样你就没有什么可以向下滚动了。但这适用于 Matt Neuberg 的示例。问题 2 - 这是为什么?

当 Scrollview 滚动时真正发生了什么

我认为合乎逻辑的做法是固定 ScrollView 的框架,内容 View 的原点(或内容偏移量)随着滚动移动,内容像窗口一样显示 ScrollView 。类似于通过滑动纸张通过墙上的孔查看纸张。

但是,根据我的阅读, ScrollView 的框架实际上是在移动,就像固定页面上的放大镜一样。

问题 3 - 有人可以解释当 ScrollView 滚动时发生了什么,哪个对象的框架原点实际上在改变,为什么要这样做?

问题 4 - 谁能用简单的英语解释如何在 (A)、(B) 和 (C) 之间设置约束?

最佳答案

工作示例

你好。首先,这是几天前编写的工作代码 ScrollView 示例。

[self addSubview:scrollView];

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

Parus 的帮助下用代码编写的约束库。

正如您所提到的, ScrollView 与其 super View 相关联。换句话说,它完全填满了它。

前面几行我设置了一个 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];

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

理论

苹果有特价Tech note对于这种情况。还有一些有趣的时刻:

To make this work with Auto Layout, the top, left, bottom, and right edges within a scroll view now mean the edges of its content view.



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

现在我会试着回答你的问题。

答案

Question 1 - Why is this?



正如您在上面看到的,这是因为 ScrollView 设置内容 View 大小而不是框架大小的自动布局。

Question 2 - why is this?



同样,事情是在内容大小。特殊的约束方法看起来像是对界面构建器的 hack。在 xib 你有正常大小的 View ,在 nib 文件实例化后你只是恢复正常,将此约束固定为 0,这意味着 内容大小 ScrollView 将等于 C View 。

Question 3 - Can someone please explain what's happening when a scrollview scrolls, which object's frame origin is actually changing, and why is it done this way?



你可以在这篇文章中找到这个过程的很好的解释:Understanding Scroll Views ,但简短的回答是: UIScroll View 通过更改 self.bounds.origin 来执行滚动观点。

Question 4 - Can anyone explain how the constraints should be set between (A), (B), and (C) in plain English?



您应该通过将所有边缘固定到内部 View 来在 ScrollView 内设置约束。换句话说, ScrollView 的所有边缘必须固定两次:通过内部 View 和 super View 。

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

关于ios - Scrollview 如何与 Autolayout 一起工作,为什么设置底部垂直空间约束使其工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15583077/

相关文章:

ios - 如何让 UI 反射(reflect) HealthKit

ios - UIDocument 不起作用

ios - 在 UIScrollView 中检测突然的滚动停止

c++ - 带有 C 接口(interface)的 Objective C 库

iphone - uiscrollview 和 uiimageview subview 中没有垂直滚动

ios - 如何在表格的开头和结尾锁定表格 View 滚动

ios - 在 segue 中与自定义过渡一起设置动画状态栏

javascript - 使用来自 obj-c 的参数发送 JSON 请求

ios - 在设计时设置的 IBInspectable 属性不保持值

objective-c - NSDictionary 选择器错误 NSString 带空格