UIView的宽高比会让systemLayoutSizeFittingSize混淆

9
好的,又是一个UITableViewCell动态高度问题,但有一点不同。不幸的是,我不能只使用iOS 8,否则问题就可以解决了。需要iOS >= 7.1。
我试图实现一个单元格,其中顶部有两个图像,下面是一个标题标签,下面是一个描述标签。我知道两个顶部图像将是正方形,因此我希望它们具有相同的大小并保持正方形纵横比,但在屏幕大小变化(如方向更改或不同设备)时调整大小。
以下是可能有助于可视化的图像(由于声誉不能包括): http://i.stack.imgur.com/kOhkl.png 注意最后一段文字后面的间隙,那里本应该没有间隙。
我根据以前应用程序中的动态高度实现了自动布局,方法如下:在UITableView中使用Auto Layout进行动态单元格布局和可变行高(成功),现在也是使用相同的方法。
我已经使用storyboards和编程方式设置了约束,但没有成功。

然而,这里有一个关键点。在调用时:

CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;

输入:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath

高度值远远大于阅读时的高度:
cell.contentView.frame

另一个问题是,如果我去掉图像的纵横比约束,并将其仅更改为显式高度约束,则可以正常工作。
对于任何愿意帮助的人,我准备了一个非常简单的示例项目,以说明问题: https://github.com/alefr/DynamicTableCellHeight 它设置使用故事板进行约束,并有一个额外的分支:ExplicitHeightConstraint,仅将图像的纵横比约束更改为高度约束。
所以问题是: 有人能看到任何使其混淆高度计算的约束错误吗?还是有任何替代建议以获得想要的结果?(尽管我想使用自动布局来处理视图)。
有很多约束正在运作中(尽管没有什么特别复杂的),因此我认为查看提供的故事板(GitHub链接)比在此明确说明它们更容易。但是,如果有人喜欢,我也可以这样做。预估的高度计算如下:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

// Don't care about memory leaks now:
DynamicTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"dynamicCell"];

[self populateCell:cell withContent:self.tableData[indexPath.row]];

// Make sure the constraints have been set up for this cell, since it may have just been created from scratch.
// Use the following lines, assuming you are setting up constraints from within the cell's updateConstraints method:
[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];

// Set the width of the cell to match the width of the table view. This is important so that we'll get the
// correct cell height for different table view widths if the cell's height depends on its width (due to
// multi-line UILabels word wrapping, etc). We don't need to do this above in -[tableView:cellForRowAtIndexPath]
// because it happens automatically when the cell is used in the table view.
// Also note, the final width of the cell may not be the width of the table view in some cases, for example when a
// section index is displayed along the right side of the table view. You must account for the reduced cell width.
cell.bounds = CGRectMake(0.0f, 0.0f, CGRectGetWidth(tableView.bounds), CGRectGetHeight(cell.bounds));

// Do the layout pass on the cell, which will calculate the frames for all the views based on the constraints.
// (Note that you must set the preferredMaxLayoutWidth on multi-line UILabels inside the -[layoutSubviews] method
// of the UITableViewCell subclass, or do it manually at this point before the below 2 lines!)
[cell setNeedsLayout];
[cell layoutIfNeeded];

// Get the actual height required for the cell's contentView
CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;

// So we can read debug values
CGRect contentFrame = cell.contentView.frame;
CGRect firstImageFrame = cell.firstArtwork.frame;
CGRect secondImageFrame = cell.secondArtwork.frame;
CGRect nameFrame = cell.name.frame;
CGRect numArtworksFrame = cell.numArtworks.frame;

// Add an extra point to the height to account for the cell separator, which is added between the bottom
// of the cell's contentView and the bottom of the table view cell.
height += 1.0f;
return height;
}

- (void)populateCell:(DynamicTableViewCell*)cell withContent:(DynamicContent*)content
{
    cell.firstArtwork.image = content.firstImage;
    cell.secondArtwork.image = content.secondImage;
    cell.name.text = content.name;
    cell.numArtworks.text = [NSString stringWithFormat:@"%@ artworks", content.numImages];
}

谢谢


2
看起来 systemLayoutSizeFittingSize 正在使用 1x 图像的内在内容大小来计算高度,而没有考虑纵横比。因此它返回的 494.5 高度实际上是使用图像的内在大小计算的高度。 - Henry T Kirk
1
是的,你说得对。示例中使用的许多图像具有相同的分辨率。我尝试了不同分辨率的图像,并获得了不同的结果。如果有人想尝试,请更新存储库并创建一个名为SizeVariations的分支。那么,如果我们想要适应整个图像,这可能就是systemLayoutSizeFittingSize的期望行为。不确定如何设置约束以“尊重”为UIImagesViews设置的约束,而无需为大小适合设置显式高度。但至少我有了一些线索。感谢您指出这一点。 - alefr
说实话,你真的应该尝试使用适合视图大小的图像,否则可能会导致屏幕外渲染,这对滚动性能有很大影响。使用核心动画分析器并打开“颜色屏幕外呈现黄色”,你就会明白我的意思了。(附言:请问您能将我的第一条评论标记为有用吗?) - Henry T Kirk
我不知道这个。在应用中,我们从服务器获取第三方图像,所以我无法事先控制其大小(尽管它们有一个纵横比约束)。但当然,一旦客户端获取到它们,我可以重新调整它们的大小。不知道这个约束在使用内在大小和异步获取图像时会如何反应。这是我需要进一步研究的问题,还有性能分析。我想把这个评论标记为有用,但我不知道如何操作。是我对技术完全不了解,还是与我的声望低有关? - alefr
将鼠标悬停在我发表的你喜欢的评论上,然后点击“UP”箭头即可。 - Henry T Kirk
我想这大概就是原因,但是当我将鼠标悬停在评论上时并没有看到箭头。这与我的声望低有关吗? - alefr
1个回答

5

我曾经遇到过相同的问题,并通过重写给定单元格中的systemLayoutSizeFittingSize函数并将纵横比约束替换为高度约束来解决它。在下面的示例中,mediaPlayerAspectRationConstraint在视图生命周期内添加到视图中,并且mediaPlayerHeightContraint将用于确定单元格的正确高度(请参见下面的代码)。

因此,我使用了

CGFloat height = [cell systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;

使用[cell.contentView systemLayoutSizeFittingSize:]的替代方法。

- (CGSize)systemLayoutSizeFittingSize:(CGSize)targetSize {
    self.mediaPlayerHeightContraint.constant = CGRectGetHeight(self.experienceMedia.frame);

    //Lets replace it with an exact height constraint (workaround).

    //To remove the Unable to simultaneously satisfy constraints. exceptions
    [self.contentView layoutIfNeeded];

    [self.experienceMedia removeConstraint:self.mediaPlayerAspectRationConstraint];
    [self.experienceMedia addConstraint:self.mediaPlayerHeightContraint];

    [self setNeedsUpdateConstraints];

    CGSize res = [self.contentView systemLayoutSizeFittingSize:targetSize];

    [self.contentView layoutIfNeeded];

    [self.experienceMedia removeConstraint:self.mediaPlayerHeightContraint];
    [self.experienceMedia addConstraint:self.mediaPlayerAspectRationConstraint];

    [self setNeedsUpdateConstraints];

    return res;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接