我的问题基本上归结为支持UILabel的动态高度的最佳方式(我想其他元素)在UITableCell 中,并且正确调整标签宽度/高度和单元格的大小旋转时的高度。
我知道如何获得UILabels的预期高度,并相应地调整高度,但是当你支持横向时,事情似乎也变得非常棘手。
我认为这可能会走layoutSubviews的路线,但我不确定你如何将它与需要计算细胞高度的表结合起来。 Another interesting post在init上手动设置框架,以确保它们根据已知数量进行计算,但这只能解决部分问题。
所以这里是我正在处理的图像,红色箭头指向动态高度标签,蓝色箭头指向将改变高度的单元格。
我设法让它正常工作,但不确定它是否是正确的方法。
cellForRowAtIndexPath
中的cell.frame始终以纵向模式显示其大小。 (即,对于iPhone应用程序,它总是报告320)。cellForRowAtIndexPath
中的tableView.frame以正确的方向显示其大小[tableView reloadData]
。我找不到任何其他方式来更新单元格和标签高度在viewDidLoad
上,我获取对每个标签字体的引用,以及与纵向中的tableView相比标签宽度百分比的浮点数
CWProductDetailDescriptionCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"DescriptionCell"];
self.descriptionLabelWidthPercentage = cell.descriptionLabel.frame.size.width / 320;
self.descriptionLabelFont = cell.descriptionLabel.font;
在heightForRowAtIndexPath
中,我使用tableView宽度和已经抓取的百分比计算单元格高度:
case TableSectionDescription:
CGFloat labelWidth = self.tableView.frame.size.width * self.descriptionLabelWidthPercentage;
CGSize newLabelFrameSize = [self sizeForString:self.product.descriptionText WithConstraint:CGSizeMake(labelWidth, MAXFLOAT) AndFont:self.descriptionLabelFont];
return newLabelFrameSize.height + kTextCellPadding;
在cellForRowAtIndexPath
中,我计算标签框架的框架并更新它
cell = [tableView dequeueReusableCellWithIdentifier:@"DescriptionCell"];
((CWProductDetailDescriptionCell *)cell).descriptionLabel.text = self.product.descriptionText;
CGRect oldFrame = ((CWProductDetailDescriptionCell *)cell).descriptionLabel.frame;
CGFloat labelWidth = self.tableView.frame.size.width * self.descriptionLabelWidthPercentage;
CGSize newLabelFrameSize = [self sizeForString:self.product.descriptionText WithConstraint:CGSizeMake(labelWidth, MAXFLOAT) AndFont:((CWProductDetailDescriptionCell *)cell).descriptionLabel.font];
((CWProductDetailDescriptionCell *)cell).descriptionLabel.frame = CGRectMake(oldFrame.origin.x, oldFrame.origin.y, labelWidth, newLabelFrameSize.height);
在didRotateFromInterfaceOrientation
中,您需要[self.tableView reloadData]
cellForRowAtIndexPath
返回单元格之后,这些似乎没有开始(不确定这是否完全正确) 因此。这样做的正确方法是什么?我已经看过很多SO问题,但没有一个完全解决这个问题。
答案 0 :(得分:12)
显然,它写出了我的巨大问题,并从计算机中休息一下来解决它。
heightForRowAtIndexPath
cellForRowAtIndexPath
中进行任何帧调整
willDisplayCell forRowAtIndexPath
中进行框架调整
请注意,这些是保留/强(ARC),并且从未添加到表本身
self.descriptionReferenceCell = [self.tableView dequeueReusableCellWithIdentifier:@"DescriptionCell"];
self.attributeReferenceCell = [self.tableView dequeueReusableCellWithIdentifier:@"AttributeCell"];
heightForRowAtIndexPath
中计算可变高度单元格我首先更新我的参考单元格的宽度,以便它显示其子视图(UILabels),然后我可以将其用于计算。然后我使用更新的标签宽度来确定标签的高度,并添加任何必要的单元格填充来计算我的单元格高度。 (请记住,只有当您的单元格与标签一起更改高度时,才需要进行这些计算。)
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
float width = UIDeviceOrientationIsPortrait(orientation) ? 320 : 480;
case TableSectionDescription:
CGRect oldFrame = self.descriptionReferenceCell.frame;
self.descriptionReferenceCell.frame = CGRectMake(oldFrame.origin.x, oldFrame.origin.y, width, oldFrame.size.height);
CGFloat referenceWidth = self.descriptionReferenceCell.descriptionLabel.frame.size.width;
CGSize newLabelFrameSize = [self sizeForString:self.product.descriptionText WithConstraint:CGSizeMake(referenceWidth, MAXFLOAT) AndFont:self.descriptionReferenceCell.descriptionLabel.font];
return newLabelFrameSize.height + kTextCellPadding;
cellForRowAtIndexPath
中,请勿依赖方向执行任何与动态布局相关的更新willDisplayCell forRowAtIndexPath
中自行更新标签高度由于表格单元格已执行layoutSubviews
,因此标签已经具有正确的宽度,我用它来计算标签的确切高度。我可以安全地更新我的标签高度。
case TableSectionDescription:
CGRect oldFrame = ((CWProductDetailDescriptionCell *)cell).descriptionLabel.frame;
CGSize newLabelFrameSize = [self sizeForString:self.product.descriptionText WithConstraint:CGSizeMake(oldFrame.size.width, MAXFLOAT) AndFont:((CWProductDetailDescriptionCell *)cell).descriptionLabel.font];
((CWProductDetailDescriptionCell *)cell).descriptionLabel.frame = CGRectMake(oldFrame.origin.x, oldFrame.origin.y, oldFrame.size.width, newLabelFrameSize.height);
break;
如果您不重新加载数据,您的可见单元格将不会更新其高度和标签。
-(void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation{
[self.tableView reloadData];
}
唯一需要指出的是,我正在使用辅助类来计算仅包装基本调用的标签高度。真的,它不再保存任何代码了(我曾经在那里有其他的东西),所以只需直接使用正常的方法。
- (CGSize) sizeForString:(NSString*)string WithConstraint:(CGSize)constraint AndFont:(UIFont *)font {
CGSize labelSize = [string sizeWithFont:font
constrainedToSize:constraint
lineBreakMode:UILineBreakModeWordWrap];
return labelSize;
}
答案 1 :(得分:0)
我同意你的大部分意见。但有几点想法:
你说:
cellForRowAtIndexPath中的cell.frame始终给出其大小 肖像模式。 (即,对于iPhone应用程序,它总是报告320)。
这不是我的经历。在我的tableView:cellForRowAtIndexPath
中,我只是加入了
NSLog(@"Cell frame.size.width=%f", cell.frame.size.width);
它根据方向报告320或480。所以,通过将我的帧计算逻辑移动到willDisplayCell中,我没有看到任何好处。
你说:
是的,你可以做到。如果您在单元格右侧的描述可能很长,您可能希望将单元格左侧的标签保持固定宽度,并利用更宽的屏幕来真正最大化您所拥有的长描述细胞的右侧。但是如果你经常在右侧有较短的描述,那么根据屏幕宽度的目标百分比进行重新排列是一个好主意。就个人而言,我可能只使用固定比率(例如,左边的标签总是占宽度的33%,右边的标签总是需要66%的单元格,而不是根据百分比转换像素),它可能会简化百分比逻辑,虽然它在功能上是等价的。)在
heightForRowAtIndexPath
我使用的计算单元格高度 tableView宽度和我已经抓住的百分比:
你说:
您无法利用您可能设置的自动调整遮罩和边距 IB
是的,我从来没有尝试过(之前我看过你描述过的相同行为),但是即使它确实有效,也不是一个好主意,因为你真的需要根据包装你的词来重新计算身高。 #39;重新使用,所以虽然它可能会让你接近,但你仍然需要重新计算高度,无论如何都要把它弄好。 (坦率地说,我只是希望我可以说"根据标签的字体和宽度以及文字自动调整高度,但这并不存在。)尽管如此,我同意你的看法&#39奇怪的是,它没有更好的工作。 Autoresize应该更优雅地处理它。
你说:
如果您不知道,您的可见细胞不会更新其高度和标签 重新加载数据。
然后您建议调用reloadData
。但是didRotateFromInterfaceOrientation
太迟了,因为我看到我的肖像UILabels在它去景观后立即闪现,但是在这里重新绘制的景观重绘之前。我不想继承UITableViewCell的子类并覆盖drawRect
,但我不确定在横向再次绘制UILabel帧之前如何重新布局我的UILabel帧。我最初尝试将其移至willRotateToInterfaceOrientation
并没有奏效,但也许我没有在那里做点什么。
答案 2 :(得分:0)
谢谢,这非常有帮助。不过,我不想在任何宽度上进行硬编码。我花了很长时间看这个问题,这就是我找到的。
基本上,有一个鸡与蛋的问题,因为表格视图需要知道单元格的高度才能进行自己的布局,但是需要为包含表格视图的几何图形配置单元格。高度可以确定。
打破这种循环依赖关系的一种方法是将宽度硬编码为320或480,这在本页的其他地方建议。不过,我不喜欢硬编码大小的想法。
另一个想法是使用原型单元预先计算所有高度。我尝试了这个,但仍然遇到计算错误的高度,因为创建UITableViewCell
只会留下一个没有为表视图的实际样式和大小配置的单元格。显然,没有办法手动为特定的表视图配置单元格;你必须让UITableView
去做。
我确定的策略是让表格视图按照正常情况执行所有操作,包括创建和配置自己的单元格,然后一旦设置了所有单元格,计算实际行高并刷新表格视图。缺点是表格视图加载了两次(一次是默认高度,另一次是正确的高度),但好处是这应该是相当干净和健壮的溶液
此示例假定您有一个实例变量_cellHeights
,它是一个数组数组,用于保存每个表视图部分的计算行高:
@interface MyViewController () {
NSArray *_cellHeights; // array of arrays (for each table view section) of numbers (row heights)
}
根据当前单元格内容编写一个计算所有单元格高度的方法:
- (void)_recalculateCellHeights
{
NSMutableArray *cellHeights = [[NSMutableArray alloc] init];
for (NSUInteger section=0; section<[self.tableView numberOfSections]; section++) {
NSMutableArray *sectionCellHeights = [[NSMutableArray alloc] init];
for (NSUInteger row=0; row<[self.tableView numberOfRowsInSection:section]; row++) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
// Set cell's width to match table view's width
CGRect cellFrame = cell.frame;
cellFrame.size.width = CGRectGetWidth(self.tableView.frame);
cell.frame = cellFrame;
CGFloat cellHeight = self.tableView.rowHeight; // default
if (cell.textLabel.numberOfLines == 0 || (cell.detailTextLabel && cell.detailTextLabel.numberOfLines == 0)) {
[cell layoutIfNeeded]; // this is necessary on iOS 7 to give the cell's text labels non-zero frames
// Calculate the height of the cell based on the text
[cell.textLabel sizeToFit];
[cell.detailTextLabel sizeToFit];
cellHeight = CGRectGetHeight(cell.textLabel.frame) + CGRectGetHeight(cell.detailTextLabel.frame);
// This is the best I can come up with for this :(
if (self.tableView.separatorStyle != UITableViewCellSeparatorStyleNone) {
cellHeight += 1.0;
}
}
[sectionCellHeights addObject:[NSNumber numberWithFloat:cellHeight]];
}
[cellHeights addObject:sectionCellHeights];
}
_cellHeights = cellHeights;
}
在-tableView:cellForRowAtIndexPath:
中,正常创建一个单元格并填写textLabel,而不执行任何布局(正如Bob在本页其他地方提到的那样:“UITableViewCells在调用它之后立即执行所有布局工作,所以你的努力充其量是徒劳的,最糟糕的是他们搞砸了“)。重要的是将标签的numberOfLines
属性设置为0以允许动态高度。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
// Assign any fonts or other adjustments which may affect height here
// Enable multiple line support so textLabel can grow vertically
cell.textLabel.numberOfLines = 0;
}
// Configure the cell...
cell.textLabel.text = [self.texts objectAtIndex:indexPath.section];
return cell;
}
在-tableView:heightForRowAtIndexPath:
中,返回预先计算的单元格高度:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (_cellHeights) {
NSArray *sectionCellHeights = [_cellHeights objectAtIndex:indexPath.section];
return [[sectionCellHeights objectAtIndex:indexPath.row] floatValue];
}
return self.tableView.rowHeight; // default
}
在-viewDidAppear:
中,触发重新计算单元格高度并刷新表格。请注意,这必须在-viewDidAppear:
时间发生,而不是-viewWillAppear:
时间,因为在-viewWillAppear:
时,尚未为表格视图的几何图形配置单元格。
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self _recalculateCellHeights];
[self.tableView reloadData];
}
在-didRotateFromInterfaceOrientation:
中,做同样的事情:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
[self _recalculateCellHeights];
[self.tableView reloadData];
}
-tableView:willDisplayCell:forRowAtIndexPath:
没有必要做任何特别的事。