一些滚动后,UITableViewdynamic单元格高度才会正确
我有一个UITableView
自定义UITableViewCell
使用自动布局在故事板中定义。 单元格有几个多行UILabels
。
UITableView
似乎正确地计算单元格高度,但对于前几个单元格高度没有正确划分标签之间。 滚动一下后,一切都按预期工作(即使是最初不正确的单元格)。
- (void)viewDidLoad { [super viewDidLoad] // ... self.tableView.rowHeight = UITableViewAutomaticDimension; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { TableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"TestCell"]; // ... // Set label.text for variable length string. return cell; }
有什么我可能会失踪,这是导致汽车布局不能够在前几次工作?
我已经创build了一个演示这种行为的示例项目 。
我不知道这是清楚的logging或不,但在返回单元格之前添加[cell layoutIfNeeded]
解决您的问题。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { TableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"TestCell"]; NSUInteger n1 = firstLabelWordCount[indexPath.row]; NSUInteger n2 = secondLabelWordCount[indexPath.row]; [cell setNumberOfWordsForFirstLabel:n1 secondLabel:n2]; [cell layoutIfNeeded]; // <- added return cell; }
在cellForRowAtIndexPath
中添加[cell layoutIfNeeded]
不适用于最初滚动到视野外的单元格。
也没有用[cell setNeedsLayout]
预处理它。
您仍然需要将某些单元格向外滚动并返回到视图中才能正确resize。
这是非常令人沮丧的,因为大多数开发者都有dynamictypes,自动布局和自我大小单元正常工作 – 除了这个烦人的情况。 这个bug会影响我所有的“更高”的表格视图控制器。
当其他类似的解决scheme没有时,这对我有用:
override func didMoveToSuperview() { super.didMoveToSuperview() layoutIfNeeded() }
这似乎是一个实际的错误,因为我非常熟悉AutoLayout和如何使用UITableViewAutomaticDimension,但是我仍然偶尔遇到这个问题。 我很高兴终于find了解决方法。
我的一个项目也有同样的经历。
为什么会发生?
在Storyboard中为某些设备devise了一些宽度的单元。 例如400px。 例如你的标签有相同的宽度。 当它从故事板加载它有宽度400px。
这是一个问题:
tableView:heightForRowAtIndexPath:
在单元布局之前调用它的子视图。
所以它计算宽度为400px的标签和单元格的高度。 但是你在屏幕上运行设备,例如320px。 这个自动计算的高度是不正确的。 仅仅因为单元格的layoutSubviews
仅在tableView:heightForRowAtIndexPath:
之后发生tableView:heightForRowAtIndexPath:
即使您在layoutSubviews
手动为label设置preferredMaxLayoutWidth
,也layoutSubviews
。
我的解决scheme
1)子类UITableView
并重写dequeueReusableCellWithIdentifier:forIndexPath:
设置单元格宽度等于表格宽度和强制单元格的布局。
- (UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [super dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath]; CGRect cellFrame = cell.frame; cellFrame.size.width = self.frame.size.width; cell.frame = cellFrame; [cell layoutIfNeeded]; return cell; }
2)子类UITableViewCell
。 在layoutSubviews
为您的标签手动设置preferredMaxLayoutWidth
。 此外,你需要手动布局contentView
,因为它不会自动布局后单元格框架(我不知道为什么,但它是)
- (void)layoutSubviews { [super layoutSubviews]; [self.contentView layoutIfNeeded]; self.yourLongTextLabel.preferredMaxLayoutWidth = self.yourLongTextLabel.width; }
我有一个类似的问题,在第一次加载,行高不计算,但经过一些滚动或转到另一个屏幕,我回到这个屏幕行计算。 在第一次加载时,我的物品从互联网加载,在第二次加载时,我的物品首先从核心数据加载,然后从互联网上重新加载,我注意到行高从互联网重新加载计算。 所以我注意到,当在segueanimation中调用tableView.reloadData()(与push和present segue相同的问题)时,行高不计算。 所以我隐藏tableview在视图初始化,并把一个活动加载器,以防止对用户的丑陋的影响,我在300ms后调用tableView.reloadData现在问题得到解决。 我认为这是一个UIKit的错误,但是这个解决方法使窍门。
我将这些行(Swift 3.0)放在我的项目加载完成处理程序中
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300), execute: { self.tableView.isHidden = false self.loader.stopAnimating() self.tableView.reloadData() })
这解释了为什么对于一些人来说,在layoutSubviews中放一个reloadData来解决这个问题
在我的情况下,UILabel的最后一行在单元格首次显示时被截断。 它发生得相当随机,正确resize的唯一方法是将单元格从视图中滚动并将其恢复。 我尝试了迄今为止显示的所有可能的解决scheme(layoutIfNeeded..reloadData),但没有为我工作。 诀窍是将“Autoshrink”设置为Minimuum Font Scale (0.5)。 试一试
为表格视图自定义单元格中的所有内容添加约束,然后估算表格视图行高度并将行高度设置为自动维度,并使用viewdid加载:
override func viewDidLoad() { super.viewDidLoad() tableView.estimatedRowHeight = 70 tableView.rowHeight = UITableViewAutomaticDimension }
要修复初始加载问题,请在自定义表格视图单元格中应用layoutIfNeeded方法:
class CustomTableViewCell: UITableViewCell { override func awakeFromNib() { super.awakeFromNib() self.layoutIfNeeded() // Initialization code } }
设置preferredMaxLayoutWidth
有助于我的情况。 我补充说
cell.detailLabel.preferredMaxLayoutWidth = cell.frame.width
在我的代码。
另请参阅单行文本在UILabel和http://openradar.appspot.com/17799811中; 占用两行 。
我尝试了这个页面中的所有解决scheme,但取消了使用大小类,然后再次检查解决了我的问题。
编辑:取消select大小类别导致故事板上的很多问题,所以我尝试了另一种解决scheme。 我在视图控制器的ViewDidLoad
和ViewWillAppear
方法中填充tableView。 这解决了我的问题。
我已经尝试了这个问题的大部分答案,并没有得到任何工作。 我发现唯一的function解决scheme是添加以下到我的UITableViewController
子类:
override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) UIView.performWithoutAnimation { tableView.beginUpdates() tableView.endUpdates() } }
UIView.performWithoutAnimation
调用是必需的,否则当视图控制器加载时,您将看到普通的表视图animation。
对于我来说,这些方法都行不通,但是我发现标签在界面生成器中有一个明确的Preferred Width
。 删除(取消选中“显式”),然后使用UITableViewAutomaticDimension
按预期工作。
我有调整标签的问题,所以我只是为了做
chatTextLabel.text = chatMessage.message chatTextLabel?.updateConstraints()设置文本后
//完整的代码
func setContent() { chatTextLabel.text = chatMessage.message chatTextLabel?.updateConstraints() let labelTextWidth = (chatTextLabel?.intrinsicContentSize().width) ?? 0 let labelTextHeight = chatTextLabel?.intrinsicContentSize().height guard labelTextWidth < originWidth && labelTextHeight <= singleLineRowheight else { trailingConstraint?.constant = trailingConstant return } trailingConstraint?.constant = trailingConstant + (originWidth - labelTextWidth) }
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ // call the method dynamiclabelHeightForText }
使用上面的方法dynamic返回行的高度。 并将相同的dynamic高度分配给您使用的标签。
-(int)dynamiclabelHeightForText:(NSString *)text :(int)width :(UIFont *)font { CGSize maximumLabelSize = CGSizeMake(width,2500); CGSize expectedLabelSize = [text sizeWithFont:font constrainedToSize:maximumLabelSize lineBreakMode:NSLineBreakByWordWrapping]; return expectedLabelSize.height; }
此代码可以帮助您find标签中文本显示的dynamic高度。
在我的情况下,单元格高度的问题发生在初始表格视图加载后,并发生用户操作(点击单元格中具有更改单元格高度效果的button)。 除非我这样做,否则我一直无法让细胞改变它的高度:
[self.tableView reloadData];
我曾尝试过
[cell layoutIfNeeded];
但是这并没有奏效。
在Swift 3中,每次更新可重用单元格的文本时都必须调用self.layoutIfNeeded()。
import UIKit import SnapKit class CommentTableViewCell: UITableViewCell { static let reuseIdentifier = "CommentTableViewCell" var comment: Comment! { didSet { textLbl.attributedText = comment.attributedTextToDisplay() self.layoutIfNeeded() //This is a fix to make propper automatic dimentions (height). } } internal var textLbl = UILabel() override func layoutSubviews() { super.layoutSubviews() if textLbl.superview == nil { textLbl.numberOfLines = 0 textLbl.lineBreakMode = .byWordWrapping self.contentView.addSubview(textLbl) textLbl.snp.makeConstraints({ (make) in make.left.equalTo(contentView.snp.left).inset(10) make.right.equalTo(contentView.snp.right).inset(10) make.top.equalTo(contentView.snp.top).inset(10) make.bottom.equalTo(contentView.snp.bottom).inset(10) }) } } }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let comment = comments[indexPath.row] let cell = tableView.dequeueReusableCell(withIdentifier: CommentTableViewCell.reuseIdentifier, for: indexPath) as! CommentTableViewCell cell.selectionStyle = .none cell.comment = comment return cell }
commentsTableView.rowHeight = UITableViewAutomaticDimension commentsTableView.estimatedRowHeight = 140
就我而言,我正在更新其他周期。 所以tableTextCell高度在labelText被设置后被更新了。 我删除了asynchronous块。
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier:Identifier, for: indexPath) // Check your cycle if update cycle is same or not // DispatchQueue.main.async { cell.label.text = nil // } }
上述解决scheme都不起作用,但是下面的build议组合了。
必须在viewDidLoad()中添加以下内容。
DispatchQueue.main.async { self.tableView.reloadData() self.tableView.setNeedsLayout() self.tableView.layoutIfNeeded() self.tableView.reloadData() }
reloadData,setNeedsLayout和layoutIfNeeded的上述组合工作,但没有任何其他。 可能是特定于项目中的单元格。 是的,不得不调用reloadData两次,使其工作。
在viewDidLoad中也设置如下
tableView.rowHeight = UITableViewAutomaticDimension tableView.estimatedRowHeight = MyEstimatedHeight
在tableView(_ tableView:UITableView,cellForRowAt indexPath:IndexPath)
cell.setNeedsLayout() cell.layoutIfNeeded()
只要确保你没有在表视图的'willdisplaycell'委托方法中设置标签文本。 在“cellForRowAtindexPath”委托方法中设置标签文本进行dynamic高度计算。
别客气 :)
在我的情况下,单元格中的堆栈视图导致了问题。 这显然是一个错误。 一旦我删除它,问题就解决了。
我遇到了这个问题,并通过移动我的视图/标签初始化代码从tableView(willDisplay cell:)
TO tableView(cellForRowAt:)
。
在我的情况下,我有一个自定义视图在我的单元格布局与自动布局,并在其重写updateConstraints
约束激活/取消激活代码。
我不得不:
- 为该视图设置高度约束,优先级为999
- 在其中调用
layoutIfNeeded
覆盖didMoveToSuperview
- 在
layoutSubviews
调用updateConstraints
我的自定义单元格中的代码
override func didMoveToSuperview() { super.didMoveToSuperview() layoutIfNeeded() } override func layoutSubviews() { super.layoutSubviews() customView.updateConstraints() } func setUpUI() { [...] let heightConstraint = NSLayoutConstraint(item: customView, attribute: .height, relatedBy: .equal, toItem: contentView, attribute: .height, multiplier: 1, constant: 0) heightConstraint.priority = UILayoutPriority(999) contentView.addConstraint(heightConstraint) }