Snapkit 與 AutoLayout 優(yōu)雅布局(一)


本文涉及內(nèi)容:

1.Autolayout與UIScrollView

2.AutoLayout與UITableViewCell

Autolayout與UIScrollView

這個(gè)問題主要是做《仿簡(jiǎn)書》首頁(yè)的時(shí)候遇到的,當(dāng)時(shí)需要在一個(gè)ScrollView里需要放多個(gè)大小不固定的Label,發(fā)現(xiàn)控件位置總是出錯(cuò)。最后我發(fā)現(xiàn)ScrollView相對(duì)于普通View存在一些區(qū)別:平時(shí)我們布局,子視圖會(huì)依賴父視圖來(lái)拉約束,但是ScrollViewcontentSize的大小是由其subview的大小來(lái)決定的,如果我們子視圖繼續(xù)依賴ScrollView來(lái)拉約束,那么就會(huì)形成相互依賴,位置無(wú)法確定。

那么怎么解決呢?當(dāng)時(shí)的解決方式是:Scrollview里面放一個(gè)ContainView,然后子視圖拉約束到ContainView,這樣ContainView的大小就可以根據(jù)子視圖來(lái)變化,Scrollview的大小根據(jù)ContainView來(lái)定。

仍然需要遵守的兩大原則:

  • scrollView內(nèi)部子控件的尺寸不能以scrollView的尺寸為參照
  • scrollView內(nèi)部的子控件的約束必須完整(子控件在水平和垂直方向用約束把ContainView撐滿,使containtView擴(kuò)展以適合它們的尺寸。例如:以前普通布局,只需要定義寬高、左、上的距離即可,但是這時(shí)候需要把下、右的距離也補(bǔ)上,不然containView不知道到底尺寸多大)

動(dòng)手實(shí)踐:

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

上述代碼中垂直方向高度為固定,那么水平方向就值得注意,下面這句話代碼是關(guān)鍵,可以確定topContainerView的具體大小,進(jìn)而確定scrollView的大小,也正是符合我們剛闡述的兩大原則

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

AutoLayout與UITableViewCell

我目前遇到的UITableViewCell主要分為三類:

  • UITableviewCell中的子控件大小確定,那么也就是說(shuō)UITableviewCell的高度、寬度可以確定,這樣的cell子控件完全可以依賴UITableviewCell來(lái)進(jìn)行布局。
  • UITableviewCell的高度為可變的。那么我們可以采取上面闡述的方法,子控件依賴ContentView來(lái)布局,與ScrollView類似。
  • 第三類是第二類的升級(jí)版,UITableviewCell的子控件會(huì)根據(jù)需要出現(xiàn)或消失

下面主要講我是怎么處理第三種情況的,示例圖如下(簡(jiǎn)書的簡(jiǎn)友動(dòng)態(tài)頁(yè)面):

對(duì)于動(dòng)態(tài)UITableViewCell,我們需要讓Cell的子控件把約束固定到ContentView上,而且要約束完整。但是簡(jiǎn)友動(dòng)態(tài)還有一個(gè)問題就是高度可變(子View有時(shí)候需要隱藏),采取的解決方案是:對(duì)約束增加優(yōu)先級(jí)的差異,對(duì)單條Constraint進(jìn)行activedeactive操作,那么意味著可以動(dòng)態(tài)的啟用或者禁用某條預(yù)置的約束。所以我們只要預(yù)先設(shè)置一條高優(yōu)先級(jí)的高度為0(或者寬度為0)的約束 然后在適當(dāng)?shù)臅r(shí)候激活它就可以了。

動(dòng)手實(shí)踐:
為了代碼簡(jiǎn)潔只寫重點(diǎn)部分

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

這段代碼只是設(shè)置了用戶名和event類型(發(fā)布文字、喜歡之類) ,可以看出來(lái)只是設(shè)置了左、上的距離,以及SizeToFit,也就是設(shè)置了高度和寬度,但是并沒有把ContentView撐滿,繼續(xù)

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

這個(gè)是要把評(píng)論內(nèi)容的Label放到了一個(gè)superView中,也就是ContainView中,然后ContainView的尺寸根據(jù)內(nèi)部Label的尺寸來(lái)變化,所以Label約束也要滿足“撐滿”ContainView。make.edges.equalTo(containView).inset(UIEdgeInsetsMake(10, 10, 10, 10)).priorityHigh()SizetoFit結(jié)合就可以約束完整,確定ContainView的尺寸。注意PriorityHigh是設(shè)置約束優(yōu)先級(jí)為750,默認(rèn)為1000。

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

這段代碼有關(guān)鍵作用,make.bottom.equalTo(contentView).offset(-10)來(lái)達(dá)到約束完整的目的,“撐滿”ContentView來(lái)確定具體尺寸。同時(shí)設(shè)置了 self.heightContraint = make.height.equalTo(0).constraint來(lái)使containView的高度為0,約束優(yōu)先級(jí)為1000.那么就是說(shuō)當(dāng)此約束activate()的時(shí)候,containView高度為零,隱藏。當(dāng)deactivate()的時(shí)候,會(huì)使用優(yōu)先級(jí)為750的約束來(lái)確定ContainView的高度。

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

代碼可以進(jìn)行下載

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容