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

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

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

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

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

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

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

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

B)视图错位(Label Expected: x= -67 Actual: x= 207)

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

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

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

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

你可以在这里下载示例项目,试着看看你能弄清楚发生了什么: https://github.com/Wirsing84/AutoLayoutProblem


当前回答

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

其他回答

[在XCode 7.2和iOS 9.2中测试]

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

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

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

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

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

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

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

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


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

我的身材有多大?即在任何给定时刻滚动视图应该有多大?2 .拓展例句我只想用屏幕一半的高度,所以

scrollview.frame = (x: 0, y:0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height / 2)

How much scrolling space do I need? ie How big is the content? e.g. the frame can be just 500 points of width, but then you can set an image with 7000 points, which would take quite some time to scroll horizontally. Or let it be exactly 500 points of width which then means no horizontal scrolling can happen. How much have you scrolled at the moment? Say your content's (or image) width was 7000 and the frame size is just 500. To get to the end of the image, you'd need to scroll 6500 points to the right. The 3rd part really doesn't affect the constraints. You can ignore that for now. Understanding it just helps how a scrollView works.

解决方案

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

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

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

If all the contents you've added into the scrollview are constrained to the edges of the scrollview, then as the content grow the scrollview will add space to accommodate. It's just that you might be using UIViews where its intrinsicContentSize is 0 so the scrollView will still complain about ambiguity of its content. However if you've used a UILabel and it has a non-empty text, then its intrinsicContentSize is set (based on Font size and text length and line breaks, etc) so the scrollView won't complain about its ambiguity.

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

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

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

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
    }
}

输出: