小心使用cell的contentView
UITableViewCell是每個(gè)iOS開(kāi)發(fā)者都必須掌握的。我們都知道,應(yīng)當(dāng)將子視圖加入其contentView,而非cell本身。但為什么這樣做,如果不這樣做有什么后果,你知道嗎?
最近在開(kāi)發(fā)中遇到一個(gè)問(wèn)題:使用contentView做insets效果時(shí),Auto Layout產(chǎn)生沖突。
起因源于需要實(shí)現(xiàn)這種效果:

即內(nèi)容與cell四邊保持距離,可以用UIEdgeInsets表示。根據(jù)以往經(jīng)驗(yàn),既然所有子視圖都被加入contentView,那么使用它做insets再合適不過(guò)了。代碼如下:
contentView.snp.makeConstraints { (make) in
make.edges.equalToSuperview().inset(UIEdgeInsets(top: 10.0, left: 5.0, bottom: 10.0, right: 5.0))
}
但運(yùn)行后的效果卻不是我們想要的:

查看console中出現(xiàn)錯(cuò)誤信息,提示布局沖突(這里我使用self-sizing cell,即cell的高度由Auto Layout自動(dòng)計(jì)算):

仔細(xì)觀察,可以發(fā)現(xiàn):UIView-Encapsulated-Layout-Height這條約束與cell的總體高度(20.0 + label高度 + 20.0)沖突,系統(tǒng)選擇打破底部約束。從字面意思來(lái)看,前者似乎也在定義cell高度。而且,經(jīng)過(guò)Google,發(fā)現(xiàn)這條約束由系統(tǒng)設(shè)置,優(yōu)先級(jí)為required,無(wú)法降級(jí)。
網(wǎng)上給出的說(shuō)法是將我們自己的約束降級(jí),避免沖突。但這種做法不符合需求:UIView-Encapsulated-Layout-Height這條約束所定義的cell高度不是我們想要的。那么,到底是哪里出了問(wèn)題呢?
經(jīng)過(guò)反復(fù)測(cè)試,終于發(fā)現(xiàn)問(wèn)題所在:只要不使用contentView做insets,就一切安好。那么,為什么不能呢?帶著強(qiáng)烈的好奇心,我翻閱文檔。文檔對(duì)于contentView的解釋是這樣的:

要點(diǎn)如下:
- cell子視圖必須加入contentView;
- 之所以必須加入contentView,是因?yàn)樵诰庉嬆J较?,cell會(huì)操作contentView。如下圖所示:

此外,還在大片的錯(cuò)誤信息中發(fā)現(xiàn)了一段非常不起眼提示:

大意為:不要修改contentView屬性translatesAutoresizingMaskIntoConstraints,否則后果自負(fù)??。
SnapKit會(huì)自動(dòng)修改視圖的這個(gè)屬性(false),怪不得會(huì)觸發(fā)這個(gè)警告。
結(jié)論
經(jīng)過(guò)上面的思考,可以得出結(jié)論:
- 子視圖都必須加入contentView
- 不要修改contentView的布局屬性,frame也好,添加約束也好,都是禁止的。
可能有人會(huì)問(wèn),這樣的話(huà)如何實(shí)現(xiàn)insets效果呢?答案很簡(jiǎn)單:創(chuàng)建一個(gè)containerView,將所有子視圖放到里面,然后把它加入contentView,再做insets。例如:
