美文网首页iOS技术点iosiOS工作系列
iOS之UIScrollview添加约束图文详解

iOS之UIScrollview添加约束图文详解

作者: codingZero | 来源:发表于2016-01-21 22:40 被阅读11709次

    前言

    在iOS开发中,autolayout是一个很强大的东西,用好了,能让你事半功倍,用不好,各种蛋疼。不过autolayout是很容易学的,笔者当时仅仅看了一个多小时的资料就轻松上手,相信比笔者厉害的大有人在。

    不过无论多简单的东西,用起来总有不如意的地方,笔者曾经在UIScrollView的约束上折腾了很久,各种报错,各种查资料,终于在笔者不断的尝试下,找到了解决办法。我相信在工作中肯定有很多跟我一样,被它折腾的死去活来的同僚,在这里,笔者将自己的理解分享给大家。

    我们先将约束分为以下几类(仅指子控件与UIScrollview之间的约束)

    • 1.间距类:既子控件到父控件上下左右的间距
    • 2.宽高类:既子控件与父控件的宽高比
    • 3.居中类:既子控件在父控件中水平或者垂直居中

    首先我们来看一个例子,在普通View上给子控件添加约束




    木有报错,很easy的事情

    接下来再看看在UIScrollView上给子控件添加约束的例子




    纳尼?同样的约束,竟然报错了,以笔者最初的理解,明明都告诉它到上下左右的间距了,为什么还会报错,难道是Xcode的bug?

    注意

    在autolayout中,所有的间距类约束,并非相对于父控件本身的,而是相对于父控件的内容视图的(比如UIScrollview的contentSize),由于普通view的内容视图与本身大小一样,所以可以看成相对于它自身

    scrollView在加载时,会自动根据内部子控件来计算contentSize的值。

    看完上面两句话,相信大家应该知道为什么会报错了吧,子控件的frame依赖了scrollview的contentSize,而contentSize的值又要根据子控件的frame来计算,那到底该怎样?所以Xcode懵了......

    在开发中我们可能会遇到这样的情况,在viewDidLoad中设置了scrollview的contentSize,但是当程序运行时,并不是自己想要的效果,就是因为scrollview根据内部子控件重新计算了contentSize的值,因此我们可以在viewDidAppear中来进行设置

    说了这么多,那我们怎么来解决这个问题呢?废话不多说,直接开撸

    UIScrollview添加约束的正确方式

    假设我们要实现如下效果



    首先添加UIScrollview到控制器view上,添加约束(这个不用笔者教吧,笔者这里是上下左右间距为0,所以运行后UIScrollview的大小与屏幕大小一样)

    这里只讲竖直滚动,水平不滚动的情况,其他情况类似,笔者相信各位的智商

    傻瓜级

    给UIScrollView添加一个唯一的子控件UIView(约束如下图所示),把这个view看成是scrollview的内容视图(暂且称它“唯一view”),以后所有的子控件都添加到这个view里面



    到父控件上下左右的间距固定(0是随便写的,自己根据需求决定),固定view的高度(800随便写的),加完约束后情况



    报错,正常,报错是因为水平方向没有确定,垂直方向是没有问题的。笔者先来解释下上面约束的含义,唯一view到scrollview内容视图的上下左右间距都是0,并且固定高度为800,那么scrollview会自动计算出contentSize.height = 0(上间距) +800 + 0(下间距) = 800,那么水平方向呢?contentSize.width = 0(左间距) + 宽度? + 0(右间距) = ?,条件不足,无法计算

    特别注意

    网上有资料显示,间距类约束其实是在设置scrollView的内边距,经过笔者的亲自验证,这种说法是不对的,笔者将间距都设置为10,然后运行程序打印了scrollView.contentInset,结果显示都是0,且contentSize宽高都多了20,所以间距是算在contentSize里的,并非contentInset

    接下来我们再来添加一个约束



    让唯一view与scrollview等宽,再看结果



    没有报错,噢耶,以后想添加什么子控件就往唯一view里添加吧,相对于唯一view添加约束就so easy了。

    也许大家会有个疑问,唯一view与scrollview等宽,那到底是多宽,不还是依赖于contentSize么?

    注意

    宽高类约束是相对于scrollview本身的,并非相对于它的内容视图的,所以scrollview有多宽,唯一view就有多宽,与contentSize无关

    至此,scrollview的contentSize就可以确定其大小了
    contentSize.height = 0(上间距) +800 + 0(下间距) = 800
    contentSize.width = 0(左间距) + 唯一view宽 + 0(右间距) = 唯一view宽(scrollview\控制器view\屏幕宽度)

    看这里!!!!

    记得把800改了,改成最后一个按钮的最大Y值(Y + height)+ 下面的间距,如图


    如果在运行时才能确定最后按钮的最大Y值,可以通过代码修改唯一view的高度约束

    正常级

    与傻瓜级类似,只是scrollview的唯一view不再固定高度,而是根据它内部的子控件的约束自动调整,如图



    我们看最后一个按钮的约束,上左右间距+固定高度已经可以确定其位置和大小了,为什么还要加个下间距?就是为了确定父控件唯一view的高度,这里唯一view的高度 = 按钮最大Y值 + 下间距

    大神级

    不要那所谓的唯一view,直接在scrollview里面添加子控件
    先添加第一个子控件(称它为“子1”)及约束



    子1距离scrollview的内容视图,上间距为10,左右间距为0



    子1与scrollview等宽,它的高度是根据它内部子控件约束确定的,这里就不贴图了,会autolayout的应该都会,不理解的就当做是固定高度,此时它的位置跟尺寸已经确定,同时scrollview.contentSize.width = 0(左间距) + 子1宽度 + 0(右间距) = 子1宽度(就是scrollview\控制器view\屏幕宽度)。

    报错是因为没有设置子1到scrollview内容视图的下间距,暂时不管它(如果看着不爽,可以先添加一个下间距约束,最后记得删除),继续往scrollview中添加下面的子视图,添加完后的效果图



    下面控件的约束都一样(不贴图了)

    • 与上面视图的垂直间距为10
    • 到scrollview的内容视图左右间距为0
    • 固定高度
      约束还是报错,报错原因与添加子1一样

    接下来添加最后一个控件



    搞定,木有错误了,我们来看看约束

    • 1.上间距为10,前面控件的位置与尺寸已经确定
      按钮Y值 = 上一个控件的最大Y值 + 10
    • 2.左右间距为10,前面已经确定了scrollview内容视图的宽度
      按钮宽度 = 内容视图宽度 - 2 * 10
      按钮X值 = 10
    • 3.固定高度40
      按钮高度 = 40
      上面的约束已经确定了按钮的frame,那么最后一个下间距就是为了确定scrollview.contentSize.height
      scrollview.contentSize.height = 按钮的Y值 + 按钮的高度 + 下间距

    到目前为止,所有子控件位置大小确定,scrollview的contentSize确定,最后来看看运行效果。


    小伙伴们学会了吗,笔者初次分享经验,不喜勿喷,有错误或者不理解的地方可以留言

    相关文章

      网友评论

      • Ko_Neko:大神 求一个Demo... 看文字还是有点不懂
      • 职业简书小菜鸟::+1::+1::+1:文章很赞~之前就遇到这种问题了。哈哈,感觉楼主写的这个界面还是不用scrollview画的好,刷新界面起来消耗比较大,:stuck_out_tongue_closed_eyes::stuck_out_tongue_closed_eyes::stuck_out_tongue_closed_eyes:
      • 攻城:按照你的来做,不能看到底部的内容,左边scrollerview的属性不用设置吗?
      • X_L_F:666。解决了我长久以来对scrollview的困惑。:kissing_heart:
      • 清無:666
      • 酷哥不回头看爆炸:之前就是卡在 scrollview 的约束问题。 看到你写的明白了。谢谢 感觉 傻瓜级的还可以,在搭建过程中不会爆红。
      • MRNAN_:很赞,尤其是最后一个解决了我的问题
      • 天真烂漫的孩子:果然厉害,解决了困扰我几天的难题!
      • 1a769827f984:很适用,正好用到这个控件,有这些疑惑
      • Z了个L:明天我再去试一试。 :smile:
      • Z了个L:看大牛写了这样两句话“没有报错,噢耶,以后想添加什么子控件就往唯一view里添加吧,相对于唯一view添加约束就so easy了”,“如果在运行时才能确定最后按钮的最大Y值,可以通过代码修改唯一view的高度以及scrollview的contentSize”, 我感觉我应该弄一个唯一view的引用,然后控件都添加到这里面,然后根据最后一个视图View的maxY来求出唯一view的高度,同时把获取到的唯一view的高度设置给scrollView的contentSize的高度。 主要今天遇到这个问题卡住了,突然想到了大牛写的这篇文章,就来和大牛探讨探讨。 之前是把控件都添加到了scrollView的引用里面去了。
        Z了个L: @codingZero 今天试了一下,确实管用,问题解决了,十分谢谢
        codingZero:@Z了个L 既然你添加了唯一view,那你子控件都添加到唯一view里面,不要加到scrollView上面
      • Z了个L:请教一个问题哦?按照傻瓜级的做法来做,我先弄好UIScrollView,然后弄一个唯一view,
        在这个唯一view里面,我从上往下,先增加了一个大的View(里面的View添加好了约束)
        这个大的View,确定了高度,假设200。
        然后在这个大的View下面接着添加View,不过这个时候,我不拖控件到storyboard或者xib里面,需要手动通过代码,frame方式,来创建View,(有时候有这种需求),
        我托线,拖了一个UIScrollView的引用, 没有托线(关于)唯一view的引用。
        然后我陆续创建了2个视图View,从上到下,把创建好的视图添加到了
        UIScrollView的引用,最后我在viewDidLayoutSubviews里面,设置UIScrollView的引用.contentSize=CGSizeMake(width, 最后一个视图View.MaxY);
        但是此时,遇到的问题就是挡住了,托不上去。看到contentSize的高度的确够了,但是
        就是挡住了,托不上去。
      • b2c7a522e27e:不错的文章!
      • 清無:不错,学习了。。。
      • 55de0856f7a4:感觉这个东西,是个人都可以看懂。写的很棒。通俗易懂。这才是真大神。赞
      • csqingyang:使用上面的方法,当小视图中的高度是动态变化的,这时候应该怎么使用代码动态设置视图的约束呢? updateConstraint 方法应该在什么时候调用? 应用场景就是:服务器返回的文字内容很长,scrollVIew 中的某一个组件的高度需要更新.
        csqingyang:@codingZero 在使用 UILabel 显示文字的时候,我设置 UILabel 所在的父视图的高度约束定高了.同时设置 UILabel 的 line 为 0,还是显示不全.
        csqingyang:@codingZero csqingyang@foxmail.com.谢谢.
        codingZero:@csqingyang 显示文字的如果是UILabel的话,只要约束加对了,不需要修改任何地方,它会自动调整大小的。至于约束怎么加,你可以留个邮箱,我发个简单的demo给你
      • Z了个Y:楼主,你好。请问子view的高度可以动态设置吗?假如view1放一个可以显示多行文本的label,该如何设置view1的约束呢
        codingZero:@Z了个Y 可以,宽度固定,高度不用设置,设置label到view底部的距离就可以了
      • 文兴:实际上一开始约束错误的原因是UIScrollView需要通过子view确定contentSize,此时根据给定的约束无法得出确定的contentSize。而之后通过autolayout计算出了确定的高度和宽度,作为UIScrollView的contentSize,就一切正常了
      • 三十六变:解决了我目前纠结的问题。
        还望LZ以后多发类似好文!
      • ddaa8dae50b0:如何在storyboard设置view的宽度与当前屏幕宽度相等, 不是写死320, 也不用左右边距0限定?
        codingZero:@Ian_He 与控制器view的宽度相等,如果是在scrollview里面,那就与scrollview宽度相等
      • Pusswzy:纯大神,写的真好
      • 令__狐冲:强,又学到了一种方法,感谢~以后我要做大神,用大神级的做法~~😄😄
      • Z了个L:开发中有没有这种情况哦,大牛,子控件和scrollView的左右约束假设不为0,假设为10,那么子控件的宽度和scrollView的宽度就没有办法设置等宽了,不能确定子控件的宽度,那么就不能确定scrollView的ContentSize的宽度,就应该会报错的, 解决办法可不可以美工固定子控件的宽度哦,然后通过左右约束10来确定scollView的ContentSize的宽度,感觉不可以吧, :blush:
        codingZero:@Z了个L 可以设置等宽,你试一下就知道了,此时scollView.ContentSize.width=10 + 子控件宽度 + 10
      • Z了个L:产生一个想法,大牛是设计第一个子控件和scrollView的左右约束为0,然后让子控件的宽度和scrollView的相等,通过scrollView的宽度来确定子控件的宽度,然后在确定scrollView的ContentSize的宽度,这样宽度方向不会报错,没问题
      • Z了个L:您好,大牛,有一个疑问,你的大神级那种方式的第一张图片,添加第一个控件,设置的约束是左边0,右边0,距离上边为0,高度那里没有添加约束,我按照你的这种方式往后面做,发现,还是会报红色,高度没有确定,推断是上一个控件的位置没有办法确定,也就无法确定按钮的Y值,我把第一个添加的控件的高度那里随便设置了一个值,添加约束就好了,按道理,按照大牛的图片一步一步操作不会有问题的哦,找您确认一下 :blush:
        Z了个L:确实,我就是用简单的UIView来试验一下的,懂了,受益匪浅 :blush:
        codingZero:@Z了个L 我第一个控件没有添加高度约束,是因为我的高度是根据内部子控件确定的,所以不需要添加,我文中有提到哦,你肯定没看仔细 :smile: ,你在测试过程中,肯定没有像我这样,在里面加那么多复杂的子控件吧,所以你的才会报错
      • 叁號選手:漂亮。
      • 二亮子:写的很好,我一直都是用的傻瓜式的,😊我写了关于xib的一系列文章,还想着以后介绍一下scrollView设置layout,看来根本没必要了,简直是班门弄斧。
      • 还嬒继续:子1高度怎么根据其子视图变化?
        codingZero:@美丽19916 设置子1的子视图与它的顶部\底部间距就可以了,前提是它子视图的高度不能依赖于它
      • a05832db24ea:很好,虽然我没看完
      • 瑞廷:不错不错啊
      • HelloYeah:楼主,厉害,大牛 :+1:
      • imhui:很好!

      本文标题:iOS之UIScrollview添加约束图文详解

      本文链接:https://www.haomeiwen.com/subject/wdwlkttx.html