我有一个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;
}
有什么我可能会遗漏,导致自动布局在前几次无法完成其工作吗?
我创建了一个sample project来演示这种行为。
我不知道这是否有明确记录,但在返回单元格之前添加[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;
}
在cell.layoutIfNeeded()
中调用cellForRowAt
在ios 10和ios 11上为我工作,但不在ios 9上。
为了在ios 9上完成这项工作,我打电话给cell.layoutSubviews()
,它就是诀窍。
我尝试了此页面中的所有解决方案,但取消选中使用大小类,然后再次检查它解决了我的问题。
编辑:取消选中大小类会导致故事板上出现很多问题,所以我尝试了另一种解决方案。我在视图控制器的viewDidLoad
和viewWillAppear
方法中填充了我的表视图。这解决了我的问题。
我有调整标签大小的问题,所以我只是想做 设置文本后,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)
}
就我而言,我正在更新其他周期。因此在设置labelText后更新了tableViewCell高度。我删除了异步块。
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
// }
}
只需确保您没有在表视图的'willdisplaycell'委托方法中设置标签文本。在“cellForRowAtindexPath”委托方法中设置标签文本以进行动态高度计算。
别客气 :)
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
// call the method dynamiclabelHeightForText
}
使用上面的方法动态返回行的高度。并为您正在使用的标签指定相同的动态高度。
-(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;
}
此代码可帮助您查找标签中文本显示的动态高度。
在我的情况下,单元格高度的问题发生在加载初始表视图之后,并且发生用户操作(点击单元格中具有更改单元格高度效果的按钮)。除非我这样做,否则我无法让细胞改变其高度:
[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
以上解决方案均无效,但以下建议的组合确实有效。
不得不在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()
当其他类似的解决方案没有时,这对我有用:
override func didMoveToSuperview() {
super.didMoveToSuperview()
layoutIfNeeded()
}
这似乎是一个真正的错误,因为我非常熟悉AutoLayout以及如何使用UITableViewAutomaticDimension,但是我偶尔会遇到这个问题。我很高兴终于找到了可以解决的问题。
就我而言,单元格中的堆栈视图导致了问题。这显然是一个错误。一旦我删除它,问题就解决了。
我遇到了这个问题并通过将我的视图/标签初始化代码FROM tableView(willDisplay cell:)
移动到tableView(cellForRowAt:)
来修复它。
问题是在我们有一个有效的行高之前,初始单元格会加载。解决方法是在视图出现时强制重新加载表。
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self.tableView reloadData];
}
以上解决方案都没有为我工作,有效的是这个神奇的方法:按此顺序调用它们:
tableView.reloadData()
tableView.layoutIfNeeded()
tableView.beginUpdates()
tableView.endUpdates()
我的tableView数据是从Web服务填充的,在我写上面行的连接的回调中。
对于仍然面临此问题的人,请尝试这条神奇的线条
tableview.scrollToRow(at: IndexPath(row: 0, section: 0), at: .top, animated: true)
只需确保在填充表格之后调用此数据,这样您就可以确定第0部分和第0行始终可用,否则它会崩溃,您可以先检查它们的可用性,希望这会有所帮助。
在[cell layoutIfNeeded]
中添加cellForRowAtIndexPath
不适用于最初滚动到视野外的单元格。
它也没有与[cell setNeedsLayout]
前面。
您仍然需要滚动某些单元格并返回视图,以便它们正确调整大小。
这非常令人沮丧,因为大多数开发人员都有动态类型,自动布局和自我调整单元正常工作 - 除了这个烦人的情况。这个bug影响了我所有的“更高”的表视图控制器。
我在我的一个项目中有相同的经验。
为什么会这样?
在故事板中设计的单元格,某些设备的宽度。例如400px。例如,您的标签具有相同的宽度。从故事板加载时,它的宽度为400px。
这是一个问题:
tableView:heightForRowAtIndexPath:
在细胞布局之前调用了它的子视图。
因此它计算了标签和单元格的高度,宽度为400px。但是你在带有屏幕的设备上运行,例如320px。并且这个自动计算的高度不正确。只是因为细胞的layoutSubviews
只发生在tableView:heightForRowAtIndexPath:
之后即使你在preferredMaxLayoutWidth
手动设置layoutSubviews
为你的标签它也没有帮助。
我的解决方案
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
。在preferredMaxLayoutWidth
中为您的标签手动设置layoutSubviews
。你还需要手动布局contentView
,因为它不会在单元格框架更改后自动布局(我不知道为什么,但它是)
- (void)layoutSubviews {
[super layoutSubviews];
[self.contentView layoutIfNeeded];
self.yourLongTextLabel.preferredMaxLayoutWidth = self.yourLongTextLabel.width;
}
我有一个类似的问题,在第一次加载时,没有计算行高,但在一些滚动或转到另一个屏幕后,我回到这个屏幕行计算。在第一次加载时,我的项目从互联网加载,在第二次加载时,我的项目首先从Core Data加载并从互联网上重新加载,我注意到行高度是在从互联网重新加载时计算的。所以我注意到当在segue动画期间调用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的最后一行被截断。它发生得非常随机,正确调整大小的唯一方法是将单元格滚出视图并将其恢复原状。我尝试了到目前为止显示的所有可能的解决方案(layoutIfNeeded..reloadData),但没有任何对我有用。诀窍是将“自动收缩”设置为Minimuum字体比例(对我而言为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
}
}
我已经尝试了这个问题的大部分答案,并且无法让它们中的任何一个起作用。我找到的唯一功能解决方案是将以下内容添加到我的UITableViewController
子类中:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
UIView.performWithoutAnimation {
tableView.beginUpdates()
tableView.endUpdates()
}
}
UIView.performWithoutAnimation
调用是必需的,否则您将在视图控制器加载时看到正常的表视图动画。
设置preferredMaxLayoutWidth
对我的情况有帮助。我补充道
cell.detailLabel.preferredMaxLayoutWidth = cell.frame.width
在我的代码中。
另请参阅Single line text takes two lines in UILabel和http://openradar.appspot.com/17799811。