
本文涉及內(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)拉約束,但是ScrollView的contentSize的大小是由其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)行active和deactive操作,那么意味著可以動(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)行下載