iOS9,为什么在dequeueReusableCellWithIdentifier中调用自定义UITableViewCell的layoutSubviews:forIndexPath如果使用systemLayoutSizeFittingSize
我的问题的更具体的描述 :在iOS9中,为什么我的自定义UITableViewCell的
layoutSubviews
被dequeueReusableCellWithIdentifier:forIndexPath
,如果使用systemLayoutSizeFittingSize
来计算heightForRowAtIndexPath
的单元格高度?因此,
layoutSubviews
在由cellForRowAtIndexPath:
返回之前被调用,并且我不能再使用layoutSubviews
来确定该单元是否在屏幕上,并且存在约束处理的几个问题。
对于复杂的自定义UITableViewCells,我更喜欢对计算和应用约束后应运行的代码使用layoutSubview
方法。 由于自定义UITableViewCell很复杂 ,我在tableView:heightForRowAtIndexPath
使用systemLayoutSizeFittingSize
来通过自动布局计算高度。 这在iOS8中完美运行,但在iOS9中打破了。
为了找出根本原因,我做了一个最小的项目,包括一个自定义的UITableViewCell,它有一个带有约束集的自定义标签:
class testCell : UITableViewCell { @IBOutlet weak var labelTest: UILabel! required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) print("testCell - init:coder") } override func layoutSubviews() { super.layoutSubviews() print("testCell - layoutSubviews 💚💚💚") } func setLabelText(text : String) { self.labelTest?.text = text } }
和tableView的dataSource:
func numberOfSectionsInTableView(tableView: UITableView) -> Int { return 1 } func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 1 } func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { if let cell = tableView.dequeueReusableCellWithIdentifier("testCell") as? testCell { cell.setLabelText(String(indexPath.row)) print("🔸🔸🔸 tableview systemLayoutSizeFittingSize for \(indexPath.row)") let size = cell.systemLayoutSizeFittingSize(CGSizeMake(tableView.bounds.size.width, 1)) return size.height } return 0 } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let char = indexPath.row > 0 ? "🔴" : "🔵" print(char+char+char+char+char+char+char+char+char+char+char+char+char+char+char+char+char+char) print("tableview start... dequeueReusableCellWithIdentifier for \(indexPath.row)") let cell = tableView.dequeueReusableCellWithIdentifier("testCell", forIndexPath: indexPath) print("tableview end... dequeueReusableCellWithIdentifier for \(indexPath.row)") print(char+char+char+char+char+char+char+char+char+char+char+char+char+char+char+char+char+char) if let cell = cell as? testCell { cell.setLabelText(String(indexPath.row)) } return cell }
日志显示layoutSubviews
在dequeueReusableCellWithIdentifier中调用:forIndexPath
testCell – init:编码器
🔸🔸🔸tableviewsystemLayoutSizeFittingSize为0
testCell – init:编码器
🔸🔸🔸tableviewsystemLayoutSizeFittingSize为0
testCell – init:编码器
🔸🔸🔸tableviewsystemLayoutSizeFittingSize为0
testCell – init:编码器
🔸🔸🔸tableviewsystemLayoutSizeFittingSize为0
🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵
tableview start … dequeueReusableCellWithIdentifier为0
testCell – init:编码器
testCell – init:编码器
🔸🔸🔸tableviewsystemLayoutSizeFittingSize为0
testCell – layoutSubviews💚💚💚
tableview end … dequeueReusableCellWithIdentifier为0
🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵
testCell – init:编码器
🔸🔸🔸tableviewsystemLayoutSizeFittingSize为0
testCell – layoutSubviews💚💚💚
让我们关注2的两行之间的日志:
tableview start … dequeueReusableCellWithIdentifier为0
testCell – init:编码器
testCell – init:编码器
🔸🔸🔸tableviewsystemLayoutSizeFittingSize为0
testCell – layoutSubviews💚💚💚
tableview end … dequeueReusableCellWithIdentifier为0
-
分析:
*第一个testCell – init:编码器由dequeueReusableCellWithIdentifier:forIndexPath
调用dequeueReusableCellWithIdentifier:forIndexPath
,由cellForRowAtIndexPath
调用
*第二个testCell – init:编码器由dequeueReusableCellWithIdentifier
调用,由heightForRowAtIndexPath
调用
*我在testCell上设置了一个断点– layoutSubviews ,并确认它是由dequeueReusableCellWithIdentifier:forIndexPath
调用的dequeueReusableCellWithIdentifier:forIndexPath
,由cellForRowAtIndexPath
调用 -
得出结论:
由于未知原因,自定义UITableViewCell的layoutSubviews
由dequeueReusableCellWithIdentifier:forIndexPath
cellForRowAtIndexPath:
dequeueReusableCellWithIdentifier:forIndexPath
调用cellForRowAtIndexPath:
寻找解决方案
-
第一个解决方案在于function:
heightForRowAtIndexPath:
如果我手动计算单元格的高度而不是使用systemLayoutSizeFittingSize
,它就可以了!dequeueReusableCellWithIdentifier:forIndexPath
不会调用layoutSubviews
。
但是这种方法并不方便,因为细胞高度的计算可能太复杂了。
以下是修改后的代码和日志:func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { return 100 // might be a very complex calculation }
🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵
tableview start … dequeueReusableCellWithIdentifier为0
testCell – init:编码器
tableview end … dequeueReusableCellWithIdentifier为0
🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵
testCell – layoutSubviews💚💚💚 -
第二个解决方案在于function:
cellForRowAtIndexPath:
我尝试使用dequeueReusableCellWithIdentifier
而不是dequeueReusableCellWithIdentifier:forIndexPath
,下面的日志显示layoutSubviews
没有在dequeueReusableCellWithIdentifier
直接调用,但它仍然在tableView:_configureCellForDisplay
调用tableView:_configureCellForDisplay
(不确定时间)。 我不能确认这是一个解决方案。
无论如何,dequeueReusableCellWithIdentifier:forIndexPath
是除dequeueReusableCellWithIdentifier
之外的推荐方法。 如果我愿意,我仍然更喜欢前一个。testCell – init:编码器
🔸🔸🔸tableviewsystemLayoutSizeFittingSize为0
testCell – init:编码器
🔸🔸🔸tableviewsystemLayoutSizeFittingSize为0
testCell – init:编码器
🔸🔸🔸tableviewsystemLayoutSizeFittingSize为0
testCell – init:编码器
🔸🔸🔸tableviewsystemLayoutSizeFittingSize为0
🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵
tableview start … dequeueReusableCellWithIdentifier为0
testCell – init:编码器
tableview end … dequeueReusableCellWithIdentifier为0
🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵🔵
testCell – init:编码器
🔸🔸🔸tableviewsystemLayoutSizeFittingSize为0
testCell – layoutSubviews💚💚💚
testCell – layoutSubviews💚💚💚
问题:
虽然我找到了2个解决方案,但它们都不令人满意。
最好的方法是我仍然可以使用systemLayoutSizeFittingSize
来计算单元格高度,并使用dequeueReusableCellWithIdentifier:forIndexPath
来获取有效的单元格。
唯一的问题是我需要确认layoutSubviews
在函数cellForRowAtIndexPath
返回之前没有被调用(在当前情况下,它由dequeueReusableCellWithIdentifier:forIndexPath
调用dequeueReusableCellWithIdentifier:forIndexPath
在函数cellForRowAtIndexPath
返回之前),这样我就可以使用这个方法对于将在计算和应用单元格约束后运行的代码,并且可以确认在到达layoutSubviews
,单元格已在屏幕上。
有没有人有任何好主意?
谢谢。