UIScrollView可滚动内容大小模糊

< p >的开发者, 我在界面生成器(Xcode 5 / iOS 7)中的自动布局遇到了麻烦。 这是非常基础和重要的,所以我认为每个人都应该知道这是如何正确工作的。如果这是Xcode中的一个bug,那它就是一个严重的bug !< / p >

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

>UIViewController
>> UIView
>>>UIScrollView
>>>>UILabel (or any other comparable UIKit Element)
UIScrollView有固定的约束,例如,每边50px(没问题)。 然后我添加一个顶部空间约束UILabel(没有问题)(我甚至可以钉标签的高度/宽度,改变什么,但应该是不必要的,由于标签的内在大小)

当我给UILabel添加一个尾随约束时,问题就开始了:

例如,尾随空间到:Superview等于:25

现在出现了两个警告——我不明白为什么:

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

B)错误的视图(标签期望:x= -67实际:x= 207

我在一个新项目中做了这个最小的例子,你可以下载,我附上了一张截图。如你所见,Interface Builder期望Label位于UIScrollView的边界之外(橙色虚线矩形)。用解决问题工具更新标签的框架,将其移到那里。

请注意:如果你用一个UIView替换UIScrollView,行为是预期的(标签的帧是正确的,根据约束)。所以看起来要么是UIScrollView有问题,要么是我错过了一些重要的东西。

当我运行的应用程序没有更新标签的框架,根据IB的建议,它的定位很好,正是它应该在的地方,UIScrollView是可滚动的。 如果我确实更新了帧,标签就会从视野中消失,UIScrollView也不会滚动

帮帮我,欧比王·克诺比!为什么是模棱两可的布局?为什么会出现这种错位的观点?

你可以在这里下载示例项目,并尝试如果你能弄清楚发生了什么: # EYZ0 < / p >

 Interface Builder中的问题说明

347706 次浏览

这个错误花了我一段时间来追踪,最初我试图固定ScrollView的大小,但错误消息清楚地说“内容大小”。我确保我从ScrollView顶部固定的所有东西也固定在底部。这样,ScrollView就可以通过查找所有对象的高度来计算它的内容高度。约束。这解决了模棱两可的内容高度,宽度相当相似…最初我在ScrollView中以X居中,但我还必须将对象固定在ScrollView的一侧。我不喜欢这个,因为iPhone6的屏幕可能更宽,但它会消除“内容宽度不明确”的错误。

看看这个非常快速和点的教程,如何让滚动视图工作,完全可滚动与自动布局。现在,唯一让我感到困惑的是,为什么滚动视图的内容大小总是大于必要的..

http://samwize.com/2014/03/14/how-to-use-uiscrollview-with-autolayout/

这是我自己回答的问题UIScrollView +居中视图+模糊可滚动内容大小+许多iPhone大小的答案。

但它也完全覆盖了你的案子!

这是最简单情况的初始状态

  1. 所有边都有0个约束的scrollView
  2. 带有固定宽度和高度约束的水平和垂直居中按钮
  3. 当然,还有烦人的警告 Has ambiguous scrollable content widthHas ambiguous scrollable content height

1

我们所要做的就是

  • 添加2个额外的约束,例如“0”为落后于和/或空间为我们的视图(请看下面的截图)。

重要的是:你必须添加尾随和/或底部约束。不是“领先和领先”——这不是工作!

2

你可以在我的< >强示例项目< / >强中检查它,演示如何修复这个问题。

注:

根据逻辑,这个动作应该导致“冲突约束”。但是没有!

我不知道为什么它工作,以及Xcode如何检测哪个约束更优先级(因为我没有为这些约束显式设置优先级)。 如果有人在下面的评论中解释一下为什么它能起作用,我会很感激的

你需要创建一个UIView作为UIScrollView的子视图,如下所示:

  • # EYZ0
  • UIView '主视图'
      <李> # EYZ0
      • UIView容器视图
        • (内容)
        • 李< / ul > < / > 李< / ul > < / > 李< / ul > < / >

        第二步是创建UIScrollView约束。我用它的superView的顶部,底部,前导和尾部约束做到了这一点。

        接下来,我添加了容器UIScrollView的约束,这就是问题开始的地方。当你放置相同的约束(顶部,底部,前面和后面)时,Storyboard会给出一个警告消息:

        “可滚动内容宽度不明确”和“可滚动内容高度不明确”

        继续上面相同的约束,并添加约束Equal HeightEqual Width到你的容器视图,相对于主要的观点你的UIScrollView。换句话说,容器视图的约束被绑定到UIScrollView的父视图。

        在此之后,您的Storyboard中将没有警告,您可以继续为子视图添加约束。

更新

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

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

  2. 对于这个contentView将上、下、左、右边距设置为0将它们固定到滚动视图的内容布局指南;

  3. 现在设置contentView的高度等于框架布局指南的高度。对宽度做同样的操作;

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

# EYZ0。

现在你可以在contentView中添加你所有的视图,scrollView中的contentSize会自动根据contentView调整大小。

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

原始(弃用)

所以我是这样整理的:
  1. UIScrollView 添加 a UIView(我们可以称之为contentView);

  2. 在这个contentView将上、下、左、右边缘设置为0(当然从scrollViewsuperView);# EYZ4;

使用contentView (UView)作为容器在UIScrollView,坚持边缘(topbottomtrailingleading)的父视图(UIScrollView)和contentView应该有equalwidthequalheight的父视图。这些都是约束条件。方法的调用:

-(void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
self.scrollView.contentSize = self.contentView.frame.size;
}

为我解决了问题。

我知道我可能迟到了,但下一个解决方案无需额外代码即可解决这类问题,只是使用故事板:

对于你的内容视图,你需要设置前/后/上/下空格到滚动视图和这不会改变内容视图框架的约束,像这样:enter image description here

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

希望这能有所帮助。

滚动视图内的所有子视图必须有约束触摸滚动视图的所有边缘,正如它在文档中解释的那样,滚动视图的高度和宽度是由子视图的度量自动计算的,这意味着你需要对宽度有尾随和领先约束,对高度有顶部和底部约束。

@Matteo Gobbi的答案是完美的,但在我的情况下,滚动视图不能滚动,我删除“对准中心Y”,并添加“高度> = 1”,滚动视图将成为可滚动的

我所做的是创建一个单独的内容视图,如下所示。

内容视图是自由形式的,可以将所有子视图添加到其中,并带有与contentView相关的约束。

UIScrollView被添加到主视图控制器。

我通过编程方式添加了通过IBOutlet链接到类的contentView,并设置了UIScrollView的contentView。

UIScrollView with ContentView via Interface Builder

我得到了同样的错误。我已经做了以下工作

  1. 视图(父视图)
  2. 滚动视图0,0600600
  3. ScrollView中的UIView: 0,0,600,600
  4. UIView包含图像视图,标签

现在为scrollView(2)和UIView(3)添加开头/结尾/顶部/底部。

选择视图(1)和视图(3)设置相同的高度和重量。它解决了我的问题。

我已经做了一个视频,这将会有所帮助:

https://www.youtube.com/watch?v=s-CPN3xZS1A

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

你通常可以通过添加一个基本的UIView到滚动视图作为一个“内容视图”来绕过这个警告,就像他们提到的,让它和你的滚动视图一样大,在这个内容视图上,设置这6个参数。

enter image description here

如您所见,您总共需要6个参数!哪一个在正常情况下,你复制了两个约束,但这是避免故事板错误的方法。

[在XCode 7.2和iOS 9.2中测试]

对我来说,抑制故事板错误和警告的方法是将滚动视图和内容视图(在我的例子中,是一个堆栈视图)的内在的大小设置为占位符。这个设置可以在故事板的大小检查器中找到。它说-设置设计时间内在内容大小只影响在界面生成器中编辑时的视图。视图在运行时没有这个固有的内容大小。

所以,我想我们设置这个不会错。

注意:在storyboard中,我已经将scrollview的所有边固定在superview上,将stackview的所有边固定在scrollview上。在我的代码中,我已经为scrollview和stackview设置了translatesAutoresizingMaskIntoConstraints为false。我还没有提到内容大小。当堆栈视图动态增长时,在故事板中设置的约束确保堆栈是可滚动的。

故事板警告快把我逼疯了,我不想为了抑制警告而水平或垂直地将事物居中。

见下图:内容视图在滚动视图中垂直和水平集中。 你有歧义错误,总是确保从你的内容视图添加到scrollview的两个是1).按钮空间到容器。2)尾随空间到屏幕截图中突出显示的约束,

这些约束的意思是在滚动中,你可以在你的内容视图高度或宽度后滚动多少。

enter image description here

它可能对你有帮助。

我想这是10秒后的作品。我在XCode 7.3中观察到它,并制作了一个视频。检查:

https://www.youtube.com/watch?v=yETZKqdaPiI

你所要做的就是添加一个宽度和高度相同的子视图到UIScrollView。然后选择ViewController并按Reset to suggested constraint。为了更清楚地理解,请查看视频。

谢谢

如果你注意到滚动条在右边滚动,但内容没有移动,那么这个事实可能值得考虑:

您还可以在滚动视图的内容和滚动视图之外的对象之间使用约束,为滚动视图的内容提供固定位置,使该内容看起来漂浮在滚动视图之上。

这是苹果的文档。例如,如果你不小心把你的顶部标签/按钮/Img/视图钉在滚动区域之外的视图(也许是一个标题或scrollView上面的东西?)而不是contentView,你会冻结你的整个contentView在适当的位置。

正如在之前的回答中提到的,你应该在滚动视图中添加一个自定义视图:

自定义视图是图像中标记的ContentView .

将所有子视图添加到内容视图。 在某些时候,你会看到滚动内容视图有模糊的内容大小警告,你应该选择内容视图并单击“解决自动布局问题”按钮(在IB布局的右下角),并选择“添加缺少的约束”选项

enter image description here

从现在开始,当你运行项目时,滚动视图将自动更新它的内容大小,不需要额外的代码。

你只需要确保有办法使一个连续 从滚动视图的顶部到底部的约束行。 (-X-X-X) < / p >

在我的例子中——一个水平滚动的滚动视图——我还需要为滚动视图的每个子视图添加宽度和高度约束,尽管苹果的技术说明没有告诉您这一点。

在Interface Builder的大小检查器中设置ViewController(持有UIScrollView的ViewController)的大小为Freeform,所有这些都应该工作。

在包含UIViewcontroller的Interface Builder中大小检查器中的自由形式设置

对我来说,添加contentView并没有像建议的那样工作。此外,由于添加了视图,它会产生开销(尽管我不认为这是一个大问题)。对我来说最有效的方法就是用关闭歧义检查代替我的scrollView。一切都布置得很好,所以我认为在像我这样简单的情况下是可以的。但是请记住,如果你的scrollView的其他约束打破了,接口构建器将警告你。

enter image description here

那些在uiscrollview而不是滚动中挣扎的人只是用你上一个视图的底部布局(在你的内容视图内部)来设置你的内容视图的底部约束。不要忘记删除中心Y的约束。

其余所有约束与上面定义的相同。Scrollview只关心从它的内容视图获取最大高度,我们将它设置为最后一个视图的底部约束,这意味着Scrollview将自动改变它的内容偏移量。

在我的例子中,最后一个视图是可用的,没有线属性= 0(自动调整它的高度取决于它的内容),所以它动态增加可用的高度,最终我们的可滚动区域增加,因为可用的底部布局与我们的内容视图的底部对齐,这迫使scrollview增加它的内容偏移。

Xcode 11 +

使用autolayout最简单的方法:

  1. 添加UIScrollView和固定它0,0,0,0到superview(或你想要的大小)
  2. 在ScrollView中添加UIView类型的容器,将0,0,0,0固定到所有4个边,并将horizontallyvertically居中。
  3. 在容器大小检查器中,将bottomalign center Y的优先级更改为250。(水平滚动更改trailingalign center X)
  4. 添加所有你需要的视图到容器(UIView)。不要忘记设置最低视图的底部约束。
  5. 选择UIScrollView,选择尺寸检查器,取消选择Content Layout Guides

我在youTube上做了一个视频 # EYZ0 < / p >

我认为这里会出现两种情况。

scrollView -内的视图

  1. 没有任何内在内容(例如UIView)
  2. 大小(例如UIStackView)

对于这两种情况下的垂直可滚动视图,您需要添加以下约束:

  1. 4个从上,左,下,右的约束。

  2. 与scrollview宽度相等(用于停止水平滚动)

对于具有自身固有内容高度的视图,您不需要任何其他约束。

enter image description here

< p > < br > 对于没有任何内在内容高度的视图,需要添加高度约束。只有当高度限制大于scrollView的高度时,视图才会滚动

enter image description here

我通过使用“解决自动布局问题”>“为所选视图添加缺少的约束”来解决我的视图的这种问题 # EYZ0 < / p >

以下两个约束条件可以解决我的问题:

trailing = Stack View.trailing - 10
bottom = Stack View.bottom + 76
< p >中: 尾随,bottom是UIScrollView的尾随,底部

Swift 4+方法:

1)设置UIScrollView的上、下、左、右边缘为0

2)在UIScrollView中添加一个UIView,并设置顶部、底部、前边距和尾随边距为0(等于UIScrollView的边距)。

3)最重要的部分是设置宽度和高度,高度限制应该有低优先级

private func setupConstraints() {


// Constraints for scrollView
scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true


// Constraints for containerView
containerView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
containerView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
containerView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
containerView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
containerView.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true


let heightConstraint = containerView.heightAnchor.constraint(equalTo: scrollView.heightAnchor)
heightConstraint.priority = UILayoutPriority(rawValue: 250)
heightConstraint.isActive = true
}

垂直滚动

  1. 添加一个带有约束0,0,0,0的UIScrollView到superview。
  2. 在ScrollView中添加一个UIView,约束为0,0,0,0到superview。
  3. 为UIScrollView添加相同宽度的约束。
  4. 添加高度到UIView。
  5. 向UIView添加带有约束的元素。
  6. 对于最靠近底部的元素,确保它有一个到UIView底部的约束。
import UIKit


class ViewController: UIViewController {


//
let scrollView: UIScrollView = {
        

let sv = UIScrollView()
sv.translatesAutoresizingMaskIntoConstraints = false
return sv
}()
    

let lipsumLbl: UILabel = { // you can use here any of your subview
        

let lbl = UILabel()
lbl.translatesAutoresizingMaskIntoConstraints = false
lbl.text = """
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce eleifend nisi sit amet mi aliquet, ut efficitur risus pellentesque. Phasellus nulla justo, scelerisque ut libero id, aliquet ullamcorper lectus. Integer id iaculis nibh. Duis feugiat mi vitae magna tincidunt, vitae consequat dui tempor. Donec blandit urna nec urna volutpat, sit amet sagittis massa fringilla. Pellentesque luctus erat nec dui luctus, sed maximus urna fermentum. Nullam purus nibh, cursus vel ex nec, pulvinar lobortis leo. Proin ac libero rhoncus, bibendum lorem sed, congue sapien. Donec commodo, est non mollis malesuada, nisi massa tempus ipsum, a varius quam lorem vitae nunc.
        

Cras scelerisque nisi dolor, in gravida ex ornare a. Interdum et malesuada fames ac ante ipsum primis in faucibus. Maecenas vitae nisl id erat sollicitudin accumsan sit amet viverra sapien. Etiam scelerisque vulputate ante. Donec magna nibh, pharetra sed pretium ac, feugiat sit amet mi. Vestibulum in ipsum vitae dui vehicula pulvinar eget ut lectus. Fusce sagittis a elit ac elementum. Fusce iaculis nunc odio, at fermentum dolor varius et. Suspendisse consectetur congue massa non gravida. Sed id elit vitae nulla aliquam euismod. Pellentesque accumsan risus dolor, eu cursus nibh semper id.
        

Vestibulum vel tortor tellus. Suspendisse potenti. Pellentesque id sapien eu augue placerat dictum. Fusce tempus ligula at diam lacinia congue in ut metus. Maecenas volutpat tellus in tellus maximus imperdiet. Integer dignissim condimentum lorem, id luctus nisi maximus at. Nulla pretium, est sit amet mollis eleifend, tellus nulla viverra dolor, ac feugiat massa risus a lectus. Pellentesque ut pulvinar urna, blandit gravida ipsum. Nullam volutpat arcu nec fringilla auctor. Integer egestas enim commodo, faucibus neque ac, rutrum magna. Vivamus tincidunt rutrum auctor. Cras at rutrum felis. Fusce elementum lorem ut pharetra venenatis.
"""
lbl.textColor = .darkGray
lbl.font = UIFont.systemFont(ofSize: 16)
lbl.numberOfLines = 0
return lbl
}()
    

//======================================================================//
//
// MARK:- viewDidLoad
//
override func viewDidLoad() {
super.viewDidLoad()
        

setupViews()
setupAutoLayout()
}
    

func setupViews() {
        

title = "ScrollView Demo"
view.backgroundColor = .white
view.addSubview(scrollView)
scrollView.addSubview(lipsumLbl)
}
    

func setupAutoLayout() {
        

scrollView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
        

lipsumLbl.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
lipsumLbl.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
lipsumLbl.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
lipsumLbl.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
}
}

输出:

enter image description here

Xcode 11+, Swift 5

如果你想知道为什么所有的答案都不再适用,确保你已经固定的内容视图(一个你已经放在滚动视图)到滚动视图的内容布局指南,而不是框架布局指南。

内容视图边缘(前导,后拉,顶部,底部)不应该像图像-框架布局指南上的前导那样被固定

Not like this

但像这样-内容布局指南。

Select Content Layout Guide from dropdown .

enter image description here

然后大部分答案都会适用于你。

现在最简单的方法是这样的:

  1. 把滚动视图放到根视图中
  2. 将滚动视图的所有边固定在父视图上
  3. 取另一个UIView(我们称之为content view)把它放到滚动视图中
  4. 将内容视图的所有边固定在滚动视图的内容布局指南上
  5. 固定内容视图的宽度等于滚动视图的框架布局指南
  6. 以任何你想要的方式固定内容视图的高度(我在下面的例子中使用了常量高度约束)

总的来说,在最简单的实现中,你的结构是这样的

enter image description here

我也有同样的问题。取消这个复选框。因为您是在代码中设置内容大小。 # EYZ0 < / p >

Xcode 11+, Swift 5。

根据@WantToKnow的回答,我解决了我的问题,我准备了视频代码

如果你仍然有UIScrollView的问题,只要关闭内容布局指南 (在xcode Interface Builder中选择你的ScrollView ->在右侧面板中选择尺寸检查器->取消选择“内容布局指南”) 或者尝试这些步骤:Xcode 11滚动视图布局 -它可以 对新的版式很有用。适用于macOS 10.15.2, Xcode 11.3.1, 05.02.2020

见截图

在我的情况下,我收到了不正确的内容大小和内容大小模糊的问题在iPad,但在iPhone的情况下工作。我在故事板中做了以下更改来解决这个问题。

  1. 在UIView中添加scrollview并添加约束前置,上,后,下到0,0,0,0。
  2. 设置滚动视图的高度根据要求,例如。One hundred.
  3. 添加UIView到滚动视图,并添加约束领先,顶部,尾部和底部0,0,0,0和对齐中心(X)和中心(Y)约束。
  4. 在滚动视图的尺寸检查器中取消选择“内容布局指南”。

使用自动布局

在一个定义良好的视图中添加滚动视图,然后在滚动视图中添加堆栈视图

从滚动视图和堆栈视图中添加上、左、右、下、中心X和中心Y约束到相关的超级视图中

确保约束从堆栈视图链接到正确的父视图(scrollview),因为当前默认设置是添加Align Top约束,而不是Top Space约束。

对于任何视图,框架和它的内容的大小是相同的,即如果你有一些内容(例如图像)的大小为200 * 800,那么它的帧也是200 * 800。

这对于scrollview的内容是不正确的。内容通常大于scrollView的帧大小。如果内容宽度相同,则只能垂直滚动。如果高度相同,则只能水平滚动。因此它是唯一需要6个约束而不是4个约束的视图。对于任何其他具有超过4个要求约束的视图都会导致冲突。


要设置你的滚动视图,它的内容和滚动的位置,你基本上需要回答三个问题:

  1. 我的身材有多大?即在任何给定时刻滚动视图应该有多大?2 .拓展例句我只想用屏幕一半的高度,所以
scrollview.frame = (x: 0, y:0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height / 2)
  1. 我需要多少滚动空间?内容有多大?例如,帧的宽度可以只有500个点,但你可以将图像设置为7000个点,这将花费相当长的时间来水平滚动。或者设置宽度为500点,这样就不会出现水平滚动。

  2. 你现在滚动了多少?假设您的内容(或图像)宽度为7000,而帧大小仅为500。为了到达图像的结尾,你需要向右滚动6500个点。第三部分并不影响约束条件。你现在可以忽略它。理解它有助于scrollView如何工作。

解决方案

基本上,如果你忽略了2个额外的约束(关于内容大小),那么布局引擎将会因为模糊性而抱怨。它不知道内容的哪些区域被隐藏(在scrollView中不可见),哪些区域的内容不被隐藏(在scrollView中可见)。

所以一定要为内容添加大小限制。要了解更多,请参阅这个答案

但有时我不添加大小约束到我的视图,它只是工作。为什么呢?

如果您添加到滚动视图中的所有内容都被限制在滚动视图的边缘,那么随着内容的增长,滚动视图将增加空间来容纳。这只是你可能会使用UIViews,它的intrinsicContentSize0,所以scrollView仍然会抱怨它的内容的模糊性。然而,如果你使用了UILabel并且它有一个非空文本,那么它的intrinsicContentSize被设置(基于字体大小和文本长度和换行符等),所以scrollView不会抱怨它的模糊性。