我有一个类似于Mail中的Compose屏幕的表格视图,其中一个单元格用于文本输入。我希望通过使用自动布局将单元格的contentView
约束到其UITextView
子视图来自动调整单元格大小。表格视图的rowHeight
和estimatedRowHeight
设置为UITableViewAutomaticDimension
。此外,文字视图的scrollEnabled
属性设置为false
,以便报告其intrinsicContentSize
。
但是,单元格不会更新其高度,因为用户输入了文本行,即使文本视图本身 更新其内在内容大小。据我所知,手动请求此方法的唯一方法是让表格视图调用beginUpdates()
和endUpdates()
。然后正确更新单元格的高度,但这也会导致整个表格视图的重新布局。这是(其他人)sample code demonstrating the problem。
如何在不布置整个表格视图的情况下反映单元格文本视图中的更改?
答案 0 :(得分:3)
reloadRowsAtIndexPaths
的问题在于UITextField
将resignFirstResponder
,因为从本质上来说,重新加载了单元格:即。{em}。毁
相反,beginUpdates()
& endUpdates()
会维护现有的单元格,但如果在包含scrollEnabled
UITextView
的单元格上调用,则会在每个textViewDidChange
触发时保持抖动。
限制更新频率
此解决方案基于流行的textViewDidChange
方法,仅通过推迟更新来完全减少或停止闪烁。
子类UITableViewCell
:
class TableViewTextViewCell : UITableViewCell, UITextViewDelegate {
var refreshCell:(() -> Void)? = nil
var textViewDirtyCount = 0
// MARK: - UITextViewDelegate
func textViewDidChange(_ textView: UITextView) {
textViewDirtyCount += 1
perform(#selector(TableViewTextViewCell.queuedTextVewDidChange),
with: nil,
afterDelay: 0.3) // Wait until typing stopped
}
func textViewDidBeginEditing(_ textView: UITextView) {
textViewDirtyCount = 0 // initialize queuedTextVewDidChange
}
func textViewDidEndEditing(_ textView: UITextView) {
textViewDirtyCount = -1 // prevent any further queuedTextVewDidChange
}
func queuedTextVewDidChange() {
if textViewDirtyCount > 0 {
textViewDirtyCount -= 1
if 0 == textViewDirtyCount, let refreshCell = refreshCell {
refreshCell()
}
}
}
}
出队&更新结束:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(
"cell", forIndexPath: indexPath) as! TableViewTextViewCell
cell.refreshCell = {
() -> Void in
tableView.beginUpdates()
tableView.endUpdates()
}
return cell
}
注意输入最后一个字符后的0.3秒延迟;如果自上次更改后经过的时间少于0.3秒,则不会进行更新。这大大减少了闪烁。
►在GitHub上找到此解决方案,并在Swift Recipes上找到其他详细信息。
答案 1 :(得分:2)
如果在调用之前禁用UIView动画,则beginUpdates方法有效。
//inside of cellForRowAtIndexPath
cell.performCellUpdates = { [unowned self] in
UIView.setAnimationsEnabled(false) //the secret sauce, prevents the jitter
self.tv.beginUpdates()
self.tv.endUpdates()
UIView.setAnimationsEnabled(true) //don't forget to turn animations back on
}
...
//inside of cell subclass
func textViewDidChange(_ textView: UITextView) {
textView.sizeToFit()
performCellUpdates()
}
为像我这样晚会的其他人发帖:)
答案 2 :(得分:1)
您无法更改现有UITableViewCell
的高度。您想告诉UITableView
该单元格无效,然后重新加载。
在重新加载单元格(cellForRowAtIndexPath
)时,您可以传递新的更新单元格。然后使用tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
reloadRowsAtIndexPaths
并不像reloadData()
那么激烈。这是一个准时的索引刷新,不需要被tableView.beginUpdates
和tableView.endUpdates
包围,您可以提供您选择的动画方法,例如.Fade
。
请参阅documentation中的reloadRowsAtIndexPaths(_:withRowAnimation:)
。
另请访问this Stack Overflow answer,其中详细介绍了更改单元格高度。