問題概述
使用的是”預估行高+自動佈局“的方法實現動態行高(適用於 iOS7.0 以後系統)。
預估行高:
self.gTV.estimatedRowHeight = 90;
self.gTV.rowHeight = UITableViewAutomaticDimension;
自動佈局,又叫 autolayout,為了使文字可以多行顯示,需要保證如下設定:
- 設定 label 的 numberoflines 為 0
- 對 label 進行上左下右的完整約束
在專案實現過程中,遇到了文字內容被截斷最後一行一小部分,無法完全顯示的問題。
為了復現專案中遇到的此問題並找到原因,做了如下嘗試:
一、新建工程
新建工程測試,cell上下約束完備,底部高度約束 contentLblBtmCon 為>=9,優先順序預設1000。發現預估行高是正常的。
效果如下:
二、嘗試復現問題
隱藏系統cell分割線:self.gTV.separatorStyle = UITableViewCellSeparatorStyleNone;
同時,在自定義cell中重寫 setFrame方法實現分割線效果,結果發現文字開始顯示不全了!
- (void)setFrame:(CGRect)frame{
frame.size.height -= 8;
[super setFrame:frame];
}
約束報錯如下:
2021-02-07 14:56:37.416314+0800 DynamicCellHeightTest[60202:8764494] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<NSLayoutConstraint:0x6000033a5810 UILabel:0x7f898d0072c0.height == 21 (active)>",
"<NSLayoutConstraint:0x6000033a59f0 UILabel:0x7f898d016da0.height >= 20.5 (active)>",
"<NSLayoutConstraint:0x6000033df2a0 V:|-(16)-[UILabel:0x7f898d0072c0] (active, names: '|':UITableViewCellContentView:0x7f898d0061f0 )>",
"<NSLayoutConstraint:0x6000033df340 V:[UILabel:0x7f898d0072c0]-(NSSpace(8))-[UILabel:0x7f898d016da0] (active)>",
"<NSLayoutConstraint:0x6000033df430 V:[UILabel:0x7f898d016da0]-(>=9)-| (active, names: '|':UITableViewCellContentView:0x7f898d0061f0 )>",
"<NSLayoutConstraint:0x6000033ddb80 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x7f898d0061f0.height == 86 (active)>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x6000033a59f0 UILabel:0x7f898d016da0.height >= 20.5 (active)>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
通過 log 可知,內容 label 的高度約束被捨棄了,因此會出現內容顯示不全的問題。模擬器執行效果:
嘗試解決
修改contentLblBtmCon優先順序為High(750)
結果還是不太行:
修改contentLblBtmCon優先順序為Low(250)
效果如下:
可見,此時內容可以顯示全了,Xcode 也不報錯了。但是內容距離 cell 底部的距離太小了,並沒有大於 9。猜測:這個底部約束因為優先順序是Low,所以被系統捨棄,使得內容可以顯示完整,同時導致內容距離 cell 底部的距離太小。
但是,當我嘗試設定底部約束的為 >= 9+8=17,再執行,居然就是我想要的效果:
分析:系統先在 setFrame 生效之前,對 cell 內的上下所有約束進行行高預估。計算拿出結果後快取。在 cell 顯示之前,setFrame 生效,此時,cell 在之前預估行高的基礎上,根據約束重新佈局,捨棄了內容 label 的高度約束,導致內容顯示不全。
當我們把底部約束的優先順序降低到 Low 時,cell 在之前預估行高的基礎上,根據約束重新佈局,捨棄的就是低優先順序的底部約束了,因此才能看到低優先順序底部約束開始生效,後來因 setFrame 減小了高度,導致底部間隔變小的效果。此時,我們將計就計,把底部約束增加 cell 間隔高度(8),即可得出我們想要的效果!
小結
重寫 cell 的 setFrame 方法改變 cell 高度來實現分割線效果時,可能導致多行 label 顯示不全,此時,可以通過降低底部約束優先順序為 Low
+ 增加底部約束的值(cell 間距),來實現想要的文字多行顯示效果。
其他解決思路
當然,為了實現行分割線效果,我們也可以在自定義 cell 的底部手動新增一個 UIView 子檢視,高度設定為 cell 間隔高度,顏色改為與 UITableview 背景色一致(與 cell 背景色不同),也能達到同樣的效果。這種方法就不會因為重寫 cell 的 setFrame 方法導致多行文字顯示不全了。但是,當 cell 有選中效果或左滑刪除效果時,相關效果就不是很好了,因為分隔線 View 屬於 cell 的一部分被一起選中或移動,看起來效果有點不太好。
好了,這次的探索就到這裡了。下面附上測試用的原始碼,歡迎 star!3Q!