美文网首页iOS开发SwiftSwifty CodingiOS开发
Snapkit 与 AutoLayout 优雅布局(一)

Snapkit 与 AutoLayout 优雅布局(一)

作者: Joy___ | 来源:发表于2016-01-05 20:42 被阅读7190次

    本文涉及内容:

    1.Autolayout与UIScrollView

    2.AutoLayout与UITableViewCell

    Autolayout与UIScrollView

    这个问题主要是做《仿简书》首页的时候遇到的,当时需要在一个ScrollView里需要放多个大小不固定的Label,发现控件位置总是出错。最后我发现ScrollView相对于普通View存在一些区别:平时我们布局,子视图会依赖父视图来拉约束,但是ScrollViewcontentSize的大小是由其subview的大小来决定的,如果我们子视图继续依赖ScrollView来拉约束,那么就会形成相互依赖,位置无法确定。

    那么怎么解决呢?当时的解决方式是:Scrollview里面放一个ContainView,然后子视图拉约束到ContainView,这样ContainView的大小就可以根据子视图来变化,Scrollview的大小根据ContainView来定。

    仍然需要遵守的两大原则:

    • scrollView内部子控件的尺寸不能以scrollView的尺寸为参照
    • scrollView内部的子控件的约束必须完整(子控件在水平和垂直方向用约束把ContainView撑满,使containtView扩展以适合它们的尺寸。例如:以前普通布局,只需要定义宽高、左、上的距离即可,但是这时候需要把下、右的距离也补上,不然containView不知道到底尺寸多大)

    动手实践:

    var topScroll: UIScrollView!
    var topContainerView = UIView()
    
    func setTopScroll(){
        topScroll = UIScrollView()
        addSubview(topScroll)
        
        topScroll.snp_makeConstraints { (make) -> Void in
            make.top.equalTo(0)
            make.height.equalTo(topScrollHeight)   //topScrollHeigh为固定值
            make.left.right.equalTo(self)
        }
        topScroll.addSubview(topContainerView)
        
        topContainerView.snp_makeConstraints { (make) -> Void in
            make.edges.equalTo(topScroll)
            make.height.equalTo(topScrollHeight)   //topScrollHeigh为固定值
        }
        
        for idx in 0..<themeArr.count{
            let label = UILabel()
            topContainerView.addSubview(label)
    
            label.snp_makeConstraints(closure: { (make) -> Void in
                    make.height.equalTo(topContainerView)
                    if idx > 0, let previousLabel = topScroll.subviews[0].subviews[idx - 1] as? UILabel {
                        make.left.equalTo(previousLabel.snp_right).offset(labelGapX * 2)
                    } else {
                        make.left.equalTo(labelGapX)   //labelGapX为固定值
                    }
                })
                if idx == themeArr.count - 1 {
                    topContainerView.snp_makeConstraints(closure: { (make) -> Void in
                        make.right.equalTo(label)
                    })
                }
            }
            
        }
    }
    

    上述代码中垂直方向高度为固定,那么水平方向就值得注意,下面这句话代码是关键,可以确定topContainerView的具体大小,进而确定scrollView的大小,也正是符合我们刚阐述的两大原则

    topContainerView.snp_makeConstraints(closure: { (make) -> Void in
                    make.right.equalTo(label)
    

    AutoLayout与UITableViewCell

    我目前遇到的UITableViewCell主要分为三类:

    • UITableviewCell中的子控件大小确定,那么也就是说UITableviewCell的高度、宽度可以确定,这样的cell子控件完全可以依赖UITableviewCell来进行布局。
    • UITableviewCell的高度为可变的。那么我们可以采取上面阐述的方法,子控件依赖ContentView来布局,与ScrollView类似。
    • 第三类是第二类的升级版,UITableviewCell的子控件会根据需要出现或消失

    下面主要讲我是怎么处理第三种情况的,示例图如下(简书的简友动态页面):

    对于动态UITableViewCell,我们需要让Cell的子控件把约束固定到ContentView上,而且要约束完整。但是简友动态还有一个问题就是高度可变(子View有时候需要隐藏),采取的解决方案是:对约束增加优先级的差异,对单条Constraint进行activedeactive操作,那么意味着可以动态的启用或者禁用某条预置的约束。所以我们只要预先设置一条高优先级的高度为0(或者宽度为0)的约束 然后在适当的时候激活它就可以了。

    动手实践:
    为了代码简洁只写重点部分

        sourceUserLabel = UILabel()
        sourceUserLabel.sizeToFit()
        contentView.addSubview(sourceUserLabel)
        sourceUserLabel.snp_makeConstraints { (make) -> Void in
            
            make.top.equalTo(contentView).offset(30)
            make.left.equalTo(contentView).offset(20)
            
        }
        
        eventLabel = UILabel()
        eventLabel.sizeToFit()
        contentView.addSubview(eventLabel)
        eventLabel.snp_makeConstraints { (make) -> Void in
            make.left.equalTo(sourceUserLabel)
            make.top.equalTo(sourceUserLabel.snp_bottom).offset(10)
        }
    

    这段代码只是设置了用户名和event类型(发布文字、喜欢之类) ,可以看出来只是设置了左、上的距离,以及SizeToFit,也就是设置了高度和宽度,但是并没有把ContentView撑满,继续

        containView = UIView()
        containView.backgroundColor = UIColor.redColor()
        contentView.addSubview(containView)
        containView.snp_makeConstraints { (make) -> Void in
            make.top.equalTo(eventLabel.snp_bottom).offset(10)
            make.left.equalTo(contentView).offset(10)
            make.right.equalTo(contentView).offset(-20)
            
        }
        
        contentLabel = UILabel()
        contentLabel.lineBreakMode = NSLineBreakMode.ByWordWrapping
        contentLabel.font = UIFont.systemFontOfSize(18)
        contentLabel.numberOfLines = 3
        contentLabel.sizeToFit()
        containView.addSubview(contentLabel)
        contentLabel.snp_makeConstraints { (make) -> Void in
            make.edges.equalTo(containView).inset(UIEdgeInsetsMake(10, 10, 10, 10)).priorityHigh()
        }
    

    这个是要把评论内容的Label放到了一个superView中,也就是ContainView中,然后ContainView的尺寸根据内部Label的尺寸来变化,所以Label约束也要满足“撑满”ContainViewmake.edges.equalTo(containView).inset(UIEdgeInsetsMake(10, 10, 10, 10)).priorityHigh()SizetoFit结合就可以约束完整,确定ContainView的尺寸。注意PriorityHigh是设置约束优先级为750,默认为1000。

    containView.snp_makeConstraints { (make) -> Void in
            self.heightContraint = make.height.equalTo(0).constraint
            make.bottom.equalTo(contentView).offset(-10)
            self.heightContraint?.deactivate()
        }
    

    这段代码有关键作用,make.bottom.equalTo(contentView).offset(-10)来达到约束完整的目的,“撑满”ContentView来确定具体尺寸。同时设置了 self.heightContraint = make.height.equalTo(0).constraint来使containView的高度为0,约束优先级为1000.那么就是说当此约束activate()的时候,containView高度为零,隐藏。当deactivate()的时候,会使用优先级为750的约束来确定ContainView的高度。

    func cellType(bool: Bool){
        
        if bool{
            self.heightContraint?.activate()
        }
        else{
            self.heightContraint?.deactivate()
        }
        
    }
    

    代码可以进行下载

    相关文章

      网友评论

      • 舒马赫:用纯代码写界面是不是有点太慢了,随便一个界面都要一大坨代码,每改点东西还要重新运行,另外由于autolayout先天原因布局速度是比较慢的,会影响帧率。推荐使用xml的布局库FlexLib,采用前端布局标准flexbox(不使用autolayout),支持热刷新,自动计算高度等。可以到这里了解详细信息:

        https://github.com/zhenglibao/FlexLib
      • imChay:文中最后一段文字的“告诉”是不是应该是“高度”
        Joy___:是的呢 ,已经修改,谢谢
      • 259ef7ae0bd1:不错 支持。有后续的文章吗
        3875e78848bf:大神可以请教一下吗?
        Joy___:@Simony 没有:smile:很久没布局了
      • 拿铁代码:码字辛苦了,写的很详细。我最近也学了这一招,不用根据string长度计算每个tableViewCell的高度了。我是把cell里的UILabel直接设置numberOfLines = 0,但是UITextView貌似没有效果,不知你有没有试过。总之就是想让cell根据UITextView里text的长度自动决定大小。而且我看你cell里的约束是加到self上的,不是说应该加到self.contentView上么?
        奉强:@拿铁代码 textview应该是要禁止掉自己的滑动属性
        Joy___:@拿铁代码 UITextView继承于UIScrollView,好像这么做 没办法搞定吧。。。你可以探索下,还没搞过 TextView 哈哈
        Joy___:@拿铁代码 对对,是应该加在contentview上:smile:当时写博客的时候,没考虑到这问题,正确的思路应该是content view
      • 心中的信念:有oc版吗。
        心中的信念:@Martin_wjl 好的👍
        Joy___:@心中的信念 没有哦 不过你可以试着转换一下😁 有问题我们可以一起解决
      • Seizens_Swift:挺喜欢的,坐等大神出简书写文章得那个界面如何实现!~~
        Joy___:@为你撑伞 一起加油。:smile:
      • 1b21b40de686:学到了,谢谢提供思路
        Joy___:@1b21b40de686 🌹
      • Easy_VO:擦,前段时间就约束scrollview 有问题,后来用的其他方案,现在🈶思路解决了
        Joy___:@Easyzhan :grin:。你可以再练习一遍
      • 花前月下:不错。支持
        Joy___:@花前月下 谢谢
      • 37dad682ef20:布局啊!
        Joy___:@快捷键 嗯嗯?

      本文标题:Snapkit 与 AutoLayout 优雅布局(一)

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