UIScrollView可滚动内容大小的歧义

624

各位开发者们, 我在Interface Builder中(Xcode 5 / iOS 7)遇到了AutoLayout的问题。 这是非常基础且重要的,因此我认为每个人都应该知道如何正确地使用它。如果这是Xcode中的一个bug,那么这是一个关键性的问题!

因此,每当我有像这样的视图层次结构时,我就会遇到麻烦:

>UIViewController
>> UIView
>>>UIScrollView
>>>>UILabel (or any other comparable UIKit Element)

UIScrollView的约束很稳定,例如:距每一侧50px(没有问题)。 然后我给UILabel添加了一个Top Space约束(没有问题)(我甚至可以固定标签的高度/宽度,这并没有改变什么,但由于标签的内在大小应该是不必要的)

当我给UILabel添加一个trailing constraint时问题就开始了:

例如,Trailing Space to: Superview Equals: 25

现在会发生两个警告 - 我不明白为什么:

A)可滚动内容大小不明确(滚动视图具有不明确的可滚动内容高度/宽度)

B)未放置正确的视图(Label Expected: x=-67 Actual:x=207)

我在全新的项目中进行了这个最小化的例子,你可以下载,并且我附上了一个屏幕截图。正如你所看到的,界面生成器希望Label坐落在UIScrollView的边界之外(橙色虚线矩形)。使用Resolve Issues Tool更新标签的框架将其移动到那里。

请注意:如果您用UIView替换UIScrollView,则行为与预期相符( Label的帧是正确的,符合约束),因此UIScrollView似乎存在问题或者我漏掉了一些重要内容。

如果我不像IB建议的那样更新标签框架,就可以很好地定位它,正好在其应该处的位置,并且UIScrollView是可滚动的。 如果我更新标签,则标签被隐藏,UIScrollView无法滚动。

帮帮我,欧比万·克诺比!为什么出现模糊的布局? 为什么视图被放错了?

你可以在这里下载示例项目并尝试找出问题: https://github.com/Wirsing84/AutoLayoutProblem

Interface Builder中问题的说明


9
尝试将UILabel放置在内容视图中,然后将标签的尾边设置为内容视图(普通UIView)。这个视频可能会对你有很大帮助:http://www.youtube.com/watch?v=PgeNPRBrB18&feature=youtu.be - erdekhayser
1
视频很棒,但是对于我来说没有修复警告。 :( - Mr Rogers
非常感谢这个视频 - 我从中学到了很多东西(相当令人惊讶)!然而,当我将视频的知识与https://dev59.com/nmMk5IYBdhLWcg3wyw_H?rq=1结合起来时,我可以修复警告。剩下的问题是:如何使scrollView的contentView大小仅为必要大小? - Wirsing
除了顶部和尾部间距之外,还要添加前导空间和底部空间。 - Ting Yi Shih
我刚刚在Size Inspector中将Ambiguity->Never Verify设置为了“不检查歧义”,这样就可以正常工作而不需要将UIView添加为子视图(实际上,这对我造成了一些问题)。我通常会在代码中完成其余工作。目前还没有发现任何问题。 - Paweł Pela
显示剩余5条评论
34个回答

1254

更新

如今,苹果意识到我们多年前解决的问题(lol_face),并将内容布局指南(Content Layout Guide)边框布局指南(Frame Layout Guide)作为UIScrollView的一部分提供。因此,您需要按照以下步骤进行:

  1. 与下面的原始响应相同;

  2. 对于这个contentView将上、下、左、右的外边距设置为0,并将它们固定在滚动视图的内容布局指南(Content Layout Guide);

  3. 现在将contentView的高度设置为Frame Layout Guide的高度。宽度也同样操作。

  4. 最后,将等高约束的优先级设置为250(如果您需要视图垂直滚动,则宽度可以水平滚动)。

完成

现在,您可以将所有视图添加到该contentView中,并且scrollViewcontentSize将根据contentView自动调整大小。

不要忘记设置从contentView中最后一个对象的底部到contentView的外边距的约束。

原始[已弃用]

所以,我是这样整理的:
  1. UIScrollView内部添加一个UIView(我们可以称之为contentView

  2. 对于这个contentView将上、下、左和右的外边距都设置为0(当然要相对于作为superViewscrollView);还要水平和垂直居中对齐;


38
除了滚动视图无法猜测内容长度并相应地重新调整其contentSize以适应内部对象之外,几乎完美。我在contentView中放置了一个tableView,其高度会通过约束发生变化,但是滚动视图不允许我滚动。 - CyberMew
29
也许这个视频能帮助解决所有未解决的问题。 - Sergio
51
这并不能使其滚动。 - dorian
66
对于那些遇到ScrollView无法滚动的问题,我通过设置contentView的底部和垂直居中对齐约束(低优先级)解决了这个问题! - jimmy0251
18
5年后,花费了3个小时试图理解为什么我的imageView没有出现在scrollView上后,我想起了自己在这里的答案并解决了问题 - 这意味着即使在iOS12上工作,这个答案仍然有效。关键部分仍然是将视图居中。 - Matteo Gobbi
显示剩余36条评论

223

Xcode 11+

使用autolayout的最简单方法:

  1. 添加UIScrollView并将其固定到父视图(或所需大小)的0,0,0,0
  2. 在ScrollView内添加UIView类型的容器,将其固定到所有4个边缘的0,0,0,0并在水平垂直方向上居中。
  3. 在容器的大小检查器中,将bottomalign center Y优先级更改为250。(对于水平滚动,请更改trailingalign center X
  4. 将您需要的所有视图添加到该容器(UIView)中。 不要忘记在最低视图上设置底部约束。
  5. 选择UIScrollView,选择大小检查器并取消选择Content Layout Guides

1
我无法设置scrollview的内容高度,但它可以正常工作。 :) - Blind Ninja
3
提示:只有当我水平和垂直移除中心,并用相等的宽度替换它(scrollView + scrollView内部的视图),并根据内容添加高度时,它才对我起作用,这意味着视图的最后一个元素需要底部约束。 在此处查看更多信息:https://dev59.com/BW855IYBdhLWcg3wjlKL#54577278 - Sean Stayns
1
Sean,我每天都在使用这个方法,当涉及到垂直和水平滚动视图滚动时,我从未遇到过任何问题。也许你忘记在最低/最后一个元素上放置底部约束了。如果没有这个,它将无法工作。 - Đorđe Nilović
15
第四点,第二句话是重点。 - Nitish
12
我来自于Android背景,可以说苹果在开发者方面做的一些事情让我感到非常困惑。 - Mina Farid
显示剩余4条评论

138

Xcode 11+,Swift 5

如果您想知道为什么所有答案都不再起作用,请确保已将内容视图(放在滚动视图内的视图)固定到滚动视图的Content Layout Guide上,而不是Frame Layout Guide。

内容视图的边缘(leading,trailing,top,bottom)不应像图像上的leading那样固定到Frame Layout Guide上:

Not like this

而应像这样固定到Content Layout Guide上:

Select Content Layout Guide from dropdown

enter image description here

然后大多数答案都将适用于您。

现在最简单的方法如下所示:

  1. 将滚动视图放入根视图
  2. 将滚动视图的所有边缘固定到父视图
  3. 取另一个UIView(称为内容视图)并将其放入滚动视图中
  4. 将内容视图的所有边缘固定到滚动视图的Content Layout Guide上
  5. 将内容视图的宽度固定为等于滚动视图的Frame Layout Guide
  • 以任何你想要的方式修复内容视图的高度(在下面的示例中,我只使用了常量高度约束)
  • 总体而言,在最简单的实现中,你的结构将如下所示

    输入图像描述


    @WantToKnow 不需要考虑高度的限制,只需将其优先级降低为低(250)。 - Reckoner
    根据@WantToKnow的回答,我解决了我的问题,我准备了视频代码Xcode 11+,Swift 5. - Ahmed Abdallah
    1
    当我为内容视图的最后一个元素添加底部约束时,这对我起作用了。 - D.Zotov
    1
    有时候我真的很讨厌苹果。他们可以在Xcode中添加新功能,但这些功能不应该改变默认行为。 - Apfelsaft
    7
    这个回答应该要更优秀。 - Nanoc
    显示剩余2条评论

    104

    这个错误让我花了一些时间来追踪,起初我尝试固定ScrollView的大小,但错误信息明确指出是“内容大小”。我确保从ScrollView顶部固定的所有内容都被固定到了底部。这样ScrollView就可以通过找到所有对象和约束的高度来计算其内容高度。这解决了模糊的内容高度问题,宽度也非常相似...最初,我将大多数内容X居中在ScrollView中,但我还必须将对象固定到ScrollView的侧面。我不喜欢这样做,因为iPhone6可能具有更宽的屏幕,但这将删除“模糊的内容宽度”错误。


    33
    谢谢您的回答!如果我理解正确,您通过将ScrollView中的每个子视图固定在顶部和底部来消除错误。但是,这并不是必要的。您只需要确保有一种方法可以从ScrollView的顶部到底部创建连续的约束线即可。[-X-X-X] 希望以上翻译清晰易懂;> - Wirsing
    2
    如果视图具有可变高度,该如何解决问题?那么我应该如何设置约束条件,以便滚动视图可以计算大小呢? - hashier
    2
    你只需要确保有一种方法可以从滚动视图的顶部到底部形成连续的约束线。 - Adam Waite
    3
    帮助我的是确保滚动视图固定在父视图上而不是顶部或底部布局指南。我删除了底部布局指南的约束,并通过菜单而不是界面构建器完成了操作。Editor -> Pin -> Bottom Space to Superview。 - Alberto Lopez
    2
    如果您希望ScrollView的宽度跟随手机屏幕或UI中的任何其他元素(如包含的UIView)变化,您需要通过使用另一个在ScrollView之外的元素进行测量并设置“等宽度”。 - Lucas van Dongen
    显示剩余7条评论

    65
    这是对我自问自答问题 UIScrollView + Centered View + Ambigous Scrollable Content Size + many iPhone sizes 的答案。
    但这也完全适用于您的情况!
    因此,以下是最简单情况的初始状态:
    1. scrollView在所有边缘都没有限制
    2. 按钮水平和垂直居中,具有固定宽度和高度约束
    3. 当然 - 烦人的警告 Has ambiguous scrollable content widthHas ambiguous scrollable content height
    我们需要做的是:
    • 添加2个附加约束,例如"0"用于我们视图的尾部底部空间(请参见下方截屏示例)。

    重要提示:必须添加尾部和/或底部约束。不是“前导和顶部”-它无效!

    您可以在我的示例项目中检查它,该项目演示了如何解决此问题。

    附言

    按照逻辑 - 这个操作应该导致“冲突约束”。但是没有!
    我不知道为什么它有效,以及Xcode如何检测哪个约束更具优先级(因为我没有明确设置这些约束的优先级)。 如果有人能在下面的评论中解释为什么它有效,我将不胜感激。

    太棒了!我最终只是创建了一个底部边缘的约束,并将优先级设置为中等或低。 - netwire
    哇!这真的一点也不合理,但是却有效!谢谢!我在视图中设置了最后一个项目的底部约束为0,然后就成功了。 - Jakob Hviid PhD

    54

    我曾经遇到过相同的问题。取消这个复选框的选择。因为你是在代码中设置内容大小。

    在此输入图片描述

    54
    您需要创建一个UIView作为UIScrollView的子视图,如下所述:
    • UIViewController
    • UIView“主视图”
      • UIScrollView
        • UIView“容器视图”
          • [您的内容]
    第二步是制作UIScrollView的约束。我使用其顶部、底部、前导和尾随约束与其superView连接。
    接下来,我添加了UIScrollView容器的约束,这就是问题的开始。当您放置相同的约束(顶部、底部、前导和尾随)时,Storyboard会显示警告消息:
    “Has ambiguous scrollable content width”和“Has ambiguous scrollable content height”
    继续使用上述相同的约束,并在容器视图主视图相关的前提下,仅添加Equal HeightEqual Width约束,而不是与您的UIScrollView相关。换句话说,容器视图的约束与UIScrollView的superview有关。
    之后,您的Storyboard将没有警告,然后您可以继续添加子视图的约束。

    2
    同意 - 我认为添加一个容器视图是最好的、最可维护的解决方案。 - Andrew Ebling
    1
    当您没有导航栏需要管理时,此解决方案将起作用。 - Leena
    它不起作用。 - Gargo

    23

    我终于弄明白了,希望这样更容易理解。

    你可以通过将一个基础UIView添加到滚动视图中作为“内容视图”,就像他们提到的那样,并将其设置为与滚动视图相同的大小,在该内容视图上设置以下6个参数,通常情况下,您需要6个总参数。这是避免Storyboard错误的方法:

    enter image description here

    正如您所看到的,您需要6个总参数!在正常情况下,您会复制2个约束,但这是避免此Storyboard错误的方法。


    1
    以上的方法对我来说几乎有效,因为我担心3种不同的屏幕宽度(4/5、6、6+),所以我将内容视图和滚动视图设置为具有相等的宽度。你是对的,这是一个重复的问题,因为我已经告诉内容视图要有0个前导和结束空间。 - Stu P.
    8
    您可以将内容视图与滚动视图之间的宽高约束设置为相等,而不是使用固定的数值。这样可以适用于所有分辨率。 - Kumar C
    我需要实现同样的功能,你能帮我设置内容视图和滚动视图的高度和宽度相等吗? - Urmi
    也许值得检查一下是否仍然需要这样做。 最近我在scrollview中设置了一个imageView,并且只需设置顶部显示的4个约束条件,就能消除Xcode 8.3中的错误。 - William T.
    1
    @Honey 这篇文章已经发布超过3年了。现在故事板的功能已经得到了很大的改进。我也注意到了现在可以没有内容视图来完成操作。我会修改我的答案。 - William T.
    显示剩余2条评论

    16
    我知道我可能有点晚了,但是下面的解决方案可以在不需要额外代码的情况下解决这种问题,只需使用storyboards:

    对于您的内容视图,您需要将前导/尾随/顶部/底部空间的约束设置为scrollview,这不会更改内容视图框架,如下所示:enter image description here

    当然,您需要为内容视图创建其他约束,以便滚动视图可以知道内容大小。例如,设置固定高度和居中。

    希望能对您有所帮助。


    10
    对我来说,添加 contentView 并没有像建议的那样奏效。此外,由于添加了视图,它会创建额外的开销(尽管我不认为这是一个大问题)。对我而言最有效的方法是只为我的scrollView关闭歧义检查。一切都布局得很好,所以我想在像我这样简单的情况下这样做是可以的。但请记住,如果您的 scrollView 的其他约束条件被打破,界面构建器将不再向您发出警告。

    输入图片描述


    10
    这只会消除警告,对于解决问题并没有任何作用。 - Predrag Manojlovic
    1
    你是我的英雄。没意识到这个存在,很高兴发现它!我尝试了“仅验证位置”,本来期望只验证位置,但却发现它还允许出现这种错误的“内容大小”问题。最好选择“永不验证”。谢谢! - Dustin
    1
    如果您是通过编程方式创建子视图,我认为这就是正确的答案。谢谢! - Florian Kusche

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接