草庐IT

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

coder 2023-07-29 原文

我试图了解 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 - Scrollview 如何与 Autolayout 一起工作,为什么设置底部垂直空间约束使其工作?的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  5. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

    我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

  6. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  7. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  8. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  9. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  10. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

随机推荐