15

所以我在 IB 中设置了我的视图,这样这个文本标签通过约束与缩略图的顶部对齐。

在此处输入图像描述

然而,正如我们所知,您不能垂直对齐 UILabel 中的文本。我的文本会根据内容的长度更新字体大小。全尺寸文本看起来很棒,而小文本在视图上显着降低。

在此处输入图像描述

现有的解决方案涉及调用 sizeToFit 或更新 uilabel 的框架以匹配文本的高度。不幸的是,后者(尽管丑陋)的解决方案不能很好地处理不应该更新框架的约束。当您需要让文本自动收缩直到被截断时,前一种解决方案基本上不起作用。(因此它不适用于有限数量的行和自动收缩)。

现在至于为什么 uilabel 的固有大小(高度)不会像宽度一样通过“大小以适合内容”设置为自然大小时更新,这超出了我的理解。似乎它绝对应该,但事实并非如此。

所以我只能寻找替代解决方案。据我所知,您可能必须在标签上设置高度约束,并在计算文本高度后调整高度常数。有人有好的解决方案吗?

4

4 回答 4

7

这个问题是真正要解决的 PITA。有效的 API 在 iOS7 中已被弃用,或者 iOS7 替换 API 已损坏,这无济于事。呸!

您的解决方案很好,但是它使用了已弃用的 API ( sizeWithFont:minFontSize:actualFontSize:forWidth:lineBreakMode:),并且封装得不是很好 - 您需要将此代码复制到您想要此行为的任何单元格或视图中。从好的方面来说,它相当有效!一个错误可能是当您进行计算时标签尚未布局,但您根据其宽度执行计算。

我建议您将此行为封装在 UILabel 子类中。通过将尺寸计算放在覆盖的intrinsicContentSize方法中,标签将自动调整尺寸。我编写了以下内容,其中包含将在 iOS6 上执行的代码,以及我的版本,使用适用于 iOS7 或更高版本的非弃用 API:

@implementation TSAutoHeightLabel

- (CGSize) intrinsicContentSize
{
    NSAssert( self.baselineAdjustment == UIBaselineAdjustmentAlignCenters, @"Please ensure you are using UIBaselineAdjustmentAlignCenters!" );

    NSAssert( self.numberOfLines == 1, @"This is only for single-line labels!" );

    CGSize intrinsicContentSize;

    if ( [self.text respondsToSelector: @selector( boundingRectWithSize:options:attributes:context: )] )
    {
        NSStringDrawingContext* context = [NSStringDrawingContext new];
        context.minimumScaleFactor = self.minimumScaleFactor;

        CGSize inaccurateSize = [self.text boundingRectWithSize: CGSizeMake( self.bounds.size.width, CGFLOAT_MAX )
                                                        options: NSStringDrawingUsesLineFragmentOrigin
                                                     attributes: @{ NSFontAttributeName : self.font }
                                                        context: context].size;

        CGSize accurateSize = [self.text sizeWithAttributes: @{ NSFontAttributeName : [UIFont fontWithName: self.font.fontName size: 12.0] } ];

        CGFloat accurateHeight = accurateSize.height * inaccurateSize.width / accurateSize.width;

        intrinsicContentSize = CGSizeMake( inaccurateSize.width, accurateHeight);
    }
    else
    {
        CGFloat actualFontSize;

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"

        [self.text sizeWithFont: self.font
                    minFontSize: self.minimumFontSize
                 actualFontSize: &actualFontSize
                       forWidth: self.frame.size.width
                  lineBreakMode: NSLineBreakByTruncatingTail];

#pragma GCC diagnostic pop

        CGRect lineBox = CTFontGetBoundingBox((__bridge CTFontRef)([UIFont fontWithName: self.font.fontName size: actualFontSize]));

        intrinsicContentSize = lineBox.size;
    }

    return intrinsicContentSize;
}

@end

这个实现并不完美。我必须确保使用baselineAdjustment == UIBaselineAdjustmentAlignCenters,而且我不能100% 确定我理解为什么。而且我对必须跳过以获得准确的文本高度的箍不满意。我的计算产生的结果和你的计算结果之间也存在一些像素差异。随意使用它并根据需要进行调整:)

boundingRectWithSize:options:attributes:contextAPI 对我来说似乎很糟糕。虽然它(大部分!)正确地将文本限制为输入大​​小,但它没有计算正确的高度!它返回的高度基于所提供字体的行高,即使正在缩放。我的猜测是这就是为什么UILabel默认情况下没有这种行为?我的解决方法是计算高度和宽度都准确的不受约束的大小,然后使用受约束和不受约束的宽度之间的比率来计算受约束大小的准确高度。什么皮塔饼。Apple 开发论坛和 SO 上有很多抱怨指出这个 API 有很多这样的问题。

于 2013-11-06T00:16:24.147 回答
2

所以我找到了一个解决方法。这有点冒险,但它有效。

所以我所做的是在 IB 中为我的文本行添加高度约束,并在我的视图中获取对它的引用。

然后在 layoutSubviews 中,我根据我必须计算的字体大小更新我的约束高度:

- (void)layoutSubviews {
    if (self.titleLabel.text) {
        CGFloat actualFontSize;
        CGSize titleSize = [self.titleLabel.text sizeWithFont:self.titleLabel.font minFontSize:9.0 actualFontSize:&actualFontSize forWidth:self.titleLabel.frame.size.width lineBreakMode:NSLineBreakByTruncatingTail];
        CGRect lineBox = CTFontGetBoundingBox((__bridge CTFontRef)([UIFont fontWithName:@"ProximaNova-Regular" size:actualFontSize]));
        self.titleHeightConstraint.constant = lineBox.size.height;
    }
    [super layoutSubviews];
}

起初我只是将它设置为实际的字体大小,但即使进行了调整 (*1.2),它仍然会裁剪较小的字体大小。关键是使用CTFontGetBoundingBox根据我的计算确定的字体大小。

这很不幸,我希望有更好的方法。也许我应该改用包装。

于 2013-03-19T22:41:57.147 回答
1

TomSwift 感谢您的回答,我真的很努力解决这个问题。

如果有人仍然有奇怪的行为,我必须改变:

内在内容大小 = CGSizeMake(不准确的大小。宽度,准确的高度);

intrinsicContentSize = CGSizeMake(inaccurateSize.width, accuracyHeight * 2);

然后它就像魅力一样。

于 2015-09-07T20:33:40.400 回答
-3

您正在寻找的是这两行代码。

myLabel.numberOfLines = 0;
myLabel.lineBreakMode = UILineBreakModeWordWrap;

您还可以在“换行符”和“行”下的“属性检查器”中找到它。

于 2013-03-19T21:37:18.463 回答