ios UITableView 动态单元格高度仅在滚动后才正确
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25971147/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
UITableView dynamic cell heights only correct after some scrolling
提问by blackp
I have a UITableView
with a custom UITableViewCell
defined in a storyboard using auto layout. The cell has several multiline UILabels
.
我有一个使用自动布局在故事板中定义UITableView
的自UITableViewCell
定义。单元格有几个多线UILabels
。
The UITableView
appears to properly calculate cell heights, but for the first few cells that height isn't properly divided between the labels.
After scrolling a bit, everything works as expected (even the cells that were initially incorrect).
在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;
}
Is there anything that I might be missing, that is causing auto layout not to be able to do its job the first few times?
有什么我可能会遗漏的,导致自动布局在前几次无法完成工作吗?
I've created a sample projectwhich demonstrates this behaviour.
我创建了一个示例项目来演示这种行为。
回答by rintaro
I don't know this is clearly documented or not, but adding [cell layoutIfNeeded]
before returning cell solves your problem.
我不知道这是否有明确记录,但是[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;
}
回答by Michael Peterson
This worked for me when other similar solutions did not:
当其他类似的解决方案没有时,这对我有用:
override func didMoveToSuperview() {
super.didMoveToSuperview()
layoutIfNeeded()
}
This seems like an actual bug since I am very familiar with AutoLayout and how to use UITableViewAutomaticDimension, however I still occasionally come across this issue. I'm glad I finally found something that works as a workaround.
这似乎是一个实际的错误,因为我非常熟悉 AutoLayout 以及如何使用 UITableViewAutomaticDimension,但是我仍然偶尔会遇到这个问题。我很高兴我终于找到了一些可以作为解决方法的东西。
回答by Scenario
Adding [cell layoutIfNeeded]
in cellForRowAtIndexPath
does not work for cells that are initially scrolled out-of-view.
添加[cell layoutIfNeeded]
在cellForRowAtIndexPath
不工作对于最初滚出的视图细胞。
Nor does prefacing it with [cell setNeedsLayout]
.
也不以[cell setNeedsLayout]
.
You still have to scroll certain cells out and back into view for them to resize correctly.
您仍然需要滚动某些单元格并返回到视图中才能正确调整大小。
This is pretty frustrating since most devs have Dynamic Type, AutoLayout and Self-Sizing Cells working properly — except for this annoying case. This bug impacts all of my "taller" table view controllers.
这非常令人沮丧,因为大多数开发人员都有动态类型、自动布局和自适应单元格正常工作——除了这个烦人的情况。这个错误影响了我所有的“更高”的表视图控制器。
回答by Vitaliy Gozhenko
I had same experience in one of my projects.
我在我的一个项目中也有同样的经历。
Why it happens?
为什么会发生?
Cell designed in Storyboard with some width for some device. For example 400px. For example your label have same width. When it loads from storyboard it have width 400px.
在 Storyboard 中设计的单元格为某些设备具有一定的宽度。例如 400 像素。例如,您的标签具有相同的宽度。当它从故事板加载时,它的宽度为 400 像素。
Here is a problem:
这里有一个问题:
tableView:heightForRowAtIndexPath:
called before cell layout it's subviews.
tableView:heightForRowAtIndexPath:
在单元格布局之前调用它的子视图。
So it calculated height for label and cell with width 400px. But you run on device with screen, for example, 320px. And this automatically calculated height is incorrect. Just because cell's layoutSubviews
happens only after tableView:heightForRowAtIndexPath:
Even if you set preferredMaxLayoutWidth
for your label manually in layoutSubviews
it not helps.
所以它计算了宽度为 400px 的标签和单元格的高度。但是您在带有屏幕的设备上运行,例如 320px。而这个自动计算的高度是不正确的。仅仅因为单元格layoutSubviews
仅在之后发生tableView:heightForRowAtIndexPath:
即使您preferredMaxLayoutWidth
在layoutSubviews
其中手动设置标签也无济于事。
My solution:
我的解决方案:
1) Subclass UITableView
and override dequeueReusableCellWithIdentifier:forIndexPath:
. Set cell width equal to table width and force cell's layout.
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) Subclass UITableViewCell
.
Set preferredMaxLayoutWidth
manually for your labels in layoutSubviews
. Also you need manually layout contentView
, because it doesn't layout automatically after cell frame change (I don't know why, but it is)
2)子类UITableViewCell
。preferredMaxLayoutWidth
为您的标签手动设置layoutSubviews
。您还需要手动布局contentView
,因为它不会在单元格框架更改后自动布局(我不知道为什么,但确实如此)
- (void)layoutSubviews {
[super layoutSubviews];
[self.contentView layoutIfNeeded];
self.yourLongTextLabel.preferredMaxLayoutWidth = self.yourLongTextLabel.width;
}
回答by alvinmeimoun
I have a similar problem, at the first load, the row height was not calculated but after some scrolling or go to another screen and i come back to this screen rows are calculated. At the first load my items are loaded from the internet and at the second load my items are loaded first from Core Data and reloaded from internet and i noticed that rows height are calculated at the reload from internet. So i noticed that when tableView.reloadData() is called during segue animation (same problem with push and present segue), row height was not calculated. So i hidden the tableview at the view initialization and put an activity loader to prevent an ugly effect to the user and i call tableView.reloadData after 300ms and now the problem is solved. I think it's a UIKit bug but this workaround make the trick.
我有一个类似的问题,在第一次加载时,没有计算行高,但在滚动或转到另一个屏幕后,我回到这个屏幕行被计算。在第一次加载时,我的项目是从 Internet 加载的,在第二次加载时,我的项目首先从 Core Data 加载并从 Internet 重新加载,我注意到行高是在从 Internet 重新加载时计算的。所以我注意到当在转场动画期间调用 tableView.reloadData() 时(推送和呈现转场的问题相同),没有计算行高。所以我在视图初始化时隐藏了 tableview 并放置了一个活动加载器以防止对用户产生丑陋的影响,我在 300ms 后调用 tableView.reloadData ,现在问题解决了。我认为这是一个 UIKit 错误,但这种解决方法可以解决问题。
I put theses lines (Swift 3.0) in my item load completion handler
我将这些行(Swift 3.0)放在我的项目加载完成处理程序中
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300), execute: {
self.tableView.isHidden = false
self.loader.stopAnimating()
self.tableView.reloadData()
})
This explain why for some people, put a reloadData in layoutSubviews solve the issue
这解释了为什么对于某些人来说,在 layoutSubviews 中放置一个 reloadData 可以解决问题
回答by bikram sapkota
Add a constraint for all content within a table view custom cell, then estimate table view row height and set row hight to automatic dimension with in a viewdid load :
为表格视图自定义单元格中的所有内容添加约束,然后估计表格视图行高并将行高设置为在 viewdid 加载中的自动尺寸:
override func viewDidLoad() {
super.viewDidLoad()
tableView.estimatedRowHeight = 70
tableView.rowHeight = UITableViewAutomaticDimension
}
To fix that initial loading issue apply layoutIfNeeded method with in a custom table view cell :
要修复该初始加载问题,请在自定义表格视图单元格中应用 layoutIfNeeded 方法:
class CustomTableViewCell: UITableViewCell {
override func awakeFromNib() {
super.awakeFromNib()
self.layoutIfNeeded()
// Initialization code
}
}
回答by Claus
In my case the last line of the UILabel was truncated when the cell was displayed for the first time. It happened pretty randomly and the only way to size it correctly was to scroll the cell out of the view and to bring it back. I tried all the possible solutions displayed so far (layoutIfNeeded..reloadData) but nothing worked for me. The trick was to set "Autoshrink"to Minimuum Font Scale(0.5 for me). Give it a try
在我的情况下,UILabel 的最后一行在第一次显示单元格时被截断。它发生得非常随机,正确调整大小的唯一方法是将单元格滚动到视图之外并将其带回来。我尝试了到目前为止显示的所有可能的解决方案(layoutIfNeeded..reloadData),但没有任何效果对我有用。诀窍是将“自动收缩”设置为最小字体比例(对我来说是 0.5)。试一试
回答by Chris Vig
I have tried most of the answers to this question and could not get any of them to work. The only functional solution I found was to add the following to my UITableViewController
subclass:
我已经尝试了这个问题的大部分答案,但无法让其中任何一个起作用。我发现的唯一功能解决方案是将以下内容添加到我的UITableViewController
子类中:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
UIView.performWithoutAnimation {
tableView.beginUpdates()
tableView.endUpdates()
}
}
The UIView.performWithoutAnimation
call is required, otherwise you will see the normal table view animation as the view controller loads.
该UIView.performWithoutAnimation
调用是必需的,否则您将在视图控制器加载时看到正常的表视图动画。
回答by JAHelia
none of the above solutions worked for me, what worked is this recipe of a magic: call them in this order:
上述解决方案都不适合我,有效的是这个魔法秘诀:按以下顺序调用它们:
tableView.reloadData()
tableView.layoutIfNeeded()
tableView.beginUpdates()
tableView.endUpdates()
tableView.reloadData()
tableView.layoutIfNeeded()
tableView.beginUpdates()
tableView.endUpdates()
my tableView data are populated from a web service, in the call back of the connection I write the above lines.
我的 tableView 数据是从 Web 服务填充的,在连接的回调中我写了上面几行。
回答by mnemonic23
calling cell.layoutIfNeeded()
inside cellForRowAt
worked for me on ios 10 and ios 11, but not on ios 9.
在 ios 10 和 ios 11 上调用cell.layoutIfNeeded()
insidecellForRowAt
对我有用,但在 ios 9 上不起作用。
to get this work on ios 9 also, I call cell.layoutSubviews()
and it did the trick.
为了在 ios 9 上也能完成这项工作,我打了电话cell.layoutSubviews()
,结果成功了。