CC
開了簡書10個月呀酸,一直說要記錄一些東西,結(jié)果總有各種借口拖延。今天不打LOL,完成開篇大任=_=
BUG
最近項(xiàng)目中遇到一個問題:對UITableViewCell采用AutoLayout自動布局碰辅,由內(nèi)容撐起高度。而內(nèi)容是需要網(wǎng)絡(luò)返回的蕉鸳,所以在一開始進(jìn)入VC乎赴,會向服務(wù)器發(fā)出請求,同時(shí)進(jìn)行了初始繪制潮尝;在接收到服務(wù)器返回?cái)?shù)據(jù)時(shí),UITableView進(jìn)行reloadData饿序,同時(shí)根據(jù)數(shù)據(jù)重新繪制勉失。而重新繪制導(dǎo)致UITableViewCell高度與之前不同,這時(shí)就報(bào)了約束沖突原探。但是UI呈現(xiàn)完全沒問題乱凿。
起始約束是在-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier初始化方法里的調(diào)用的顽素;而當(dāng)網(wǎng)絡(luò)數(shù)據(jù)返回時(shí),UITableView reloadData徒蟆;-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath方法會調(diào)用cell中一個塞數(shù)據(jù)的方法胁出,在塞數(shù)據(jù)方法最后會調(diào)用setNeedsUpdateConstraints方法;而在-(void)updateConstraints方法中段审,會根據(jù)塞入的數(shù)據(jù)全蝶,來進(jìn)行約束的update,這里用的Masnory庫寺枉,調(diào)用的方法是- (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *make))block抑淫。這個約束沖突就報(bào)在了-(void)updateConstraints方法中。
分析與解決
根據(jù)AutoLayout的報(bào)錯姥闪,發(fā)現(xiàn)其他都沒毛病始苇,就最后一項(xiàng)有問題:height == 190.5?沃德法克筐喳?打開Debug View看一眼:
就說不可能這么矮=_=,190.5是初始沒有內(nèi)容時(shí)的高度避归,而289.5是網(wǎng)絡(luò)數(shù)據(jù)返回后蓄氧,根據(jù)數(shù)據(jù)重布局的高度。那么沖突應(yīng)該就是報(bào)在這個位置了槐脏。
一個腦殘的猜測
就是是不是因?yàn)榫W(wǎng)絡(luò)數(shù)據(jù)返回太快了喉童,太短時(shí)間內(nèi)多次更改constrains,再updateConstraints會導(dǎo)致沖突顿天?(網(wǎng)絡(luò)數(shù)據(jù)回來100ms以內(nèi)堂氯,好吧,不快=_=)
妥牌废,setNeedsUpdateConstraints延時(shí)1秒執(zhí)行咽白。
果然不報(bào)錯了!BUT鸟缕!文字圖片之間的間距被擠壓晶框,重疊到了一起是鬧哪樣啊懂从?
但是重疊的畫面出現(xiàn)后授段,再次reloadData,就沒問題了番甩。請求一次數(shù)據(jù)要reloadData兩次侵贵?這不是一名合格的程序員應(yīng)該做的事情,你知道reloadData有多耗費(fèi)資源霸笛Α(- 不知道窍育。 - 小伙子卡睦,可以的,你被開了)漱抓!
一個不腦殘的事實(shí)
debug view Hierarchy里面給的清清楚楚表锻,contentView.height約束的prority是1000。所以就是你在高度190.5時(shí)候乞娄,撐高contentView瞬逊,超出了190.5導(dǎo)致約束沖突報(bào)錯了唄。于是:
解決方案一——撲街
把撐高contentView的約束全部設(shè)置priority為999补胚。
哈哈哈码耐!果然不報(bào)錯了。
=_=||| ?尷尬溶其,約束是不沖突了骚腥,但是UI呈現(xiàn)有問題了,又把我文字之間的間隔給抹掉了瓶逃,再戰(zhàn)束铭!
解決方案二——撲街
直接拿掉contentView的height約束,沒有這個約束厢绝,看你還怎么沖突契沫,嘿嘿嘿。
在updateConstraints方法開始的伊始昔汉,用一個for循環(huán)懈万,判斷去除contentView的height約束。
結(jié)果靶病,很遺憾会通,不僅約束報(bào)錯,UI呈現(xiàn)也完全不對了娄周。
是不是沒去除干凈涕侈?打個斷點(diǎn),po一下contentView的constrains煤辨,發(fā)現(xiàn)果然還有東西:
哦裳涛,看來UITableView不是用AutoLayout布局的,這個AutoresizingMask有點(diǎn)可疑众辨,查端三,給朕徹查!
學(xué)習(xí)了一下Autoresizing泻轰,三種自適應(yīng)布局方式之一(另外兩種是layoutsubview中手動布局和autolayout)技肩,我的理解就是frame布局上增加了一定的自適應(yīng)。
然后——UITableView用的Frame布局8∩P樾觥!滾動動畫巴啦巴啦的也是靠著frame和bounds來做的S净印H蝗!屉符!厲害了word哥>缃!4V印K粝恪!來一波傳送——https://www.objc.io/issues/3-views/scroll-view/
建議objc的東西都學(xué)習(xí)一下啦吨艇,爆強(qiáng)躬它!英語沒過六級的同學(xué)(沒錯,就是我)可以搜下Objc中國咯东涡。
解決方案三——撲街
修改contentView.height約束的prority為999冯吓。
其實(shí)根據(jù)方案二的結(jié)果已經(jīng)猜到了——不僅約束報(bào)錯,UI呈現(xiàn)也完全不對了疮跑。其他cell 的contentview.height.prority都是1000组贺,就你自己是999,坑定有問題啊祖娘。當(dāng)然失尖,可以所有cell的contentview.height.prority都設(shè)為999,但是考慮cell復(fù)用問題渐苏,改動面太大掀潮,所以就放棄了。
正確解決方案
回到一開始的腦殘猜測整以。延時(shí)setNeedsUpdateConstraints執(zhí)行胧辽,再次reloaddata,就沒問題公黑。那么UITableViewCell設(shè)置contentView的height的時(shí)間應(yīng)該是在updateConstrains之后邑商,所以導(dǎo)致高度沒有實(shí)時(shí)變化,從而沖突(- 機(jī)智如我凡蚜! - 其實(shí)一開始報(bào)約束沖突的時(shí)候就該想到的人断,報(bào)沖突在updateConstrains,但是高度沒有實(shí)時(shí)更新朝蜘。- 你走開6衤酢)。
然后就查了一下setNeedsUpdateConstraints機(jī)制,然后就引出了setNeedsLayout的問題暇仲。兩者的區(qū)別是個毛毛步做?
從stackoverflow上找到一份答案,鏈接沒存奈附,大概意思就是——兩者都是表示需要重新布局全度,而且都是標(biāo)記需要,并不會立即執(zhí)行斥滤,會在下一個循環(huán)里執(zhí)行将鸵;setNeedsUpdateConstraints對應(yīng)-(void)updateConstraints,setNeedsLayout對應(yīng)- (void)layoutSubviews佑颇。BUT顶掉,setNeedsUpdateConstraints會調(diào)用-(void)updateConstraints之后再調(diào)用- (void)layoutSubviews,而setNeedsLayout只會調(diào)用- (void)layoutSubviews(本人親測這樣子的)挑胸。
我的理解:setNeedsUpdateConstraints就是更新約束痒筒,setNeedsLayout就是更新frame,約束的底層其實(shí)就是更改frame嗜暴,所以setNeedsUpdateConstraints會同時(shí)調(diào)用兩者(- 不對凸克。- 你留言。)闷沥。
說了這么多萎战,終于到解決方案了。最終方案是還在stackoverflow上找到的舆逃,一個來自于三年前的帖子——http://stackoverflow.com/questions/19132908/auto-layout-constraints-issue-on-ios7-in-uitableviewcell蚂维。
這篇帖子給了兩個解決方案,一個是設(shè)置:self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
另外一個是:在更新約束之前路狮,設(shè)置self.contentView.bounds = CGRectMake(0, 0, 99999, 99999);
第一種方案我這邊不可行虫啥,根據(jù)方案二里的log,contentview的寬奄妨、高已經(jīng)設(shè)置了Autoresizing涂籽,這個有毛用?反正我設(shè)置了砸抛,沒用评雌。
第二種方案。直焙。景东。
(- 啊啊啊啊啊啊奔誓!直接改高度斤吐,我他喵的怎么就沒想到!! - 因?yàn)槟隳槾蟆?- 臉大有福和措!你羨慕不來的庄呈!啊啊啊啊>势拧抒痒!好氣盎仙堋0涔印!傀广! - 颁独。。伪冰。)
昂誓酒,在更新約束之前,直接把contentView的寬度贮聂、高度值設(shè)置為大于等于約束之后的寬高就解決問題了靠柑。UITableViewCell給contentView計(jì)算并設(shè)置寬高是在- (void)layoutSubviews方法中進(jìn)行的,而-(void)updateConstraints在- (void)layoutSubviews之前執(zhí)行吓懈,從而更新約束超出之前寬高值歼冰,導(dǎo)致沖突;而在-(void)updateConstraints之前擴(kuò)增一下寬高耻警,就不會沖突隔嫡,而在- (void)layoutSubviews方法中又重新計(jì)算了高度賦值,所以UI呈現(xiàn)也沒問題甘穿。
BUT腮恩,為毛設(shè)置高度超出約束之后應(yīng)得的值就沒問題咧(如本例中,需要更新約束前温兼,設(shè)置contentView.height的值大于等于289.5)秸滴?約束把高度撐起來不行?壓縮反而可以募判?講道理的話荡含,不應(yīng)該設(shè)置的剛剛好才行么?
一位老司機(jī)告訴我兰伤,可能是這個樣子的:
你把高度設(shè)低了内颗,那么你布局的時(shí)候就超出去了,布局都完不成敦腔;而你把高度設(shè)高了均澳,那么你布局最起碼能在這個view里完成。
BUT,我的約束是從上到下找前,從top到bottom串聯(lián)起來的糟袁,你設(shè)置height高了,肯定會有一個部分被拉伸躺盛,而所有的priority都是1000项戴,講道理的話,也應(yīng)該報(bào)沖突啊槽惫。
求老司機(jī)解答 =_=
總結(jié)
stackoverflow果然是一個神奇的網(wǎng)站周叮,三年前的解答竟然解決了我的問題,古人誠不我欺也界斜。
objc要好好學(xué)習(xí)仿耽,英文水平有待提升(現(xiàn)在屏幕分兩半,一半objc各薇,一半百度翻譯)项贺。
最后,竟然寫了這么多峭判,LOL一把去开缎,有問題請留言,擼完之后看=_=林螃。