我的问题的更具体的描述:在iOS9中,
layoutSubviews
调用我的自定义UITableViewCell的dequeueReusableCellWithIdentifier:forIndexPath
,如果systemLayoutSizeFittingSize
用于计算heightForRowAtIndexPath
中的单元格高度?因此,在
layoutSubviews
返回之前调用cellForRowAtIndexPath:
,我无法再使用layoutSubviews
来确定该单元格是否在屏幕上,并且有几个约束处理的问题。
对于复杂的自定义UITableViewCells,我更倾向于使用layoutSubview
方法来计算和应用约束后运行的代码。
由于自定义UITableViewCell是复杂,我使用systemLayoutSizeFittingSize
中的tableView:heightForRowAtIndexPath
来通过自动布局计算高度。
这在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
}
日志显示在dequeueReusableCellWithIdentifier中调用layoutSubviews
:forIndexPath
testCell - init:编码器
tableview systemLayoutSizeFittingSize为0
testCell - init:编码器
tableview systemLayoutSizeFittingSize为0
testCell - init:编码器
tableview systemLayoutSizeFittingSize为0
testCell - init:编码器
tableview systemLayoutSizeFittingSize为0
tableview start ... dequeueReusableCellWithIdentifier为0
testCell - init:编码器
testCell - init:编码器
tableview systemLayoutSizeFittingSize为0
testCell - layoutSubviews
tableview end ... dequeueReusableCellWithIdentifier为0
testCell - init:编码器
tableview systemLayoutSizeFittingSize为0
testCell - layoutSubviews
让我们关注以下两行之间的日志:
tableview start ... dequeueReusableCellWithIdentifier for 0
testCell - init:编码器
testCell - init:编码器
tableview systemLayoutSizeFittingSize为0
testCell - layoutSubviews
tableview end ... dequeueReusableCellWithIdentifier for 0
分析:
*第一个 testCell - init:coder 由dequeueReusableCellWithIdentifier:forIndexPath
调用,由cellForRowAtIndexPath
调用
*第二个 testCell - init:coder 由dequeueReusableCellWithIdentifier
调用,由heightForRowAtIndexPath
调用
*我在 testCell - layoutSubviews 设置了一个断点,并确认它由dequeueReusableCellWithIdentifier:forIndexPath
调用cellForRowAtIndexPath
由此得出结论:
由于未知原因,layoutSubviews
dequeueReusableCellWithIdentifier:forIndexPath
会调用自定义UITableViewCell的cellForRowAtIndexPath:
第一个解决方案在于功能:heightForRowAtIndexPath:
如果我手动计算单元格的高度而不是使用systemLayoutSizeFittingSize
,它就可以了! layoutSubviews
无法呼叫dequeueReusableCellWithIdentifier:forIndexPath
但是这种方法并不方便,因为细胞高度的计算可能太复杂了
以下是修改后的代码和日志:
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
第二个解决方案在于功能:cellForRowAtIndexPath:
我尝试使用dequeueReusableCellWithIdentifier
代替dequeueReusableCellWithIdentifier:forIndexPath
,下面的日志显示layoutSubviews
未直接在dequeueReusableCellWithIdentifier
中调用,但仍会在tableView:_configureCellForDisplay
中调用({1}}不确定时间)。我无法确认它是否是一种解决方案
无论如何,dequeueReusableCellWithIdentifier:forIndexPath
是dequeueReusableCellWithIdentifier
以外的推荐方法。如果我愿意,我仍然更喜欢前者。
testCell - init:编码器
tableview systemLayoutSizeFittingSize为0
testCell - init:编码器
tableview systemLayoutSizeFittingSize为0
testCell - init:编码器
tableview systemLayoutSizeFittingSize为0
testCell - init:编码器
tableview systemLayoutSizeFittingSize为0
tableview start ... dequeueReusableCellWithIdentifier为0
testCell - init:编码器
tableview end ... dequeueReusableCellWithIdentifier为0
testCell - init:编码器
tableview systemLayoutSizeFittingSize为0
testCell - layoutSubviews
testCell - layoutSubviews
虽然我找到了2个解决方案,但它们都不令人满意
最好的方法是我仍然可以使用systemLayoutSizeFittingSize
来计算单元格高度,并使用dequeueReusableCellWithIdentifier:forIndexPath
来获取有效单元格。
唯一的问题是我需要确认layoutSubviews
在函数cellForRowAtIndexPath
返回之前未被调用(在当前情况下,它由函数返回之前由dequeueReusableCellWithIdentifier:forIndexPath
调用cellForRowAtIndexPath
),以便我可以将此方法用于在计算和应用单元格约束后运行的代码,并且可以在到达layoutSubviews
时确认细胞已经在屏幕上
有没有人有任何好主意?
谢谢。