垂直对齐UILabel文本与约束,没有换行(自动布局,单行)

时间:2013-03-19 21:33:55

标签: ios autolayout

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

enter image description here

但是,正如我们所知,您无法垂直对齐UILabel中的文本。我的文本根据内容的长度更新字体大小。全尺寸文本看起来很棒,而视图上的小文本明显较低。

enter image description here

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

现在,为什么uilabel的内在尺寸(高度)没有像宽度那样更新,当它被设置为它的自然尺寸时,通过“尺寸适合内容”超出了我。似乎应该这样,但事实并非如此。

所以我一直在寻找其他解决方案。据我所见,您可能需要在标签上设置高度约束,并在计算文本高度后调整高度常量。任何人都有一个很好的解决方案?

4 个答案:

答案 0 :(得分:7)

这个问题是真正的PITA要解决的问题。在iOS7中不推荐使用API​​,或者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:context API看起来很糟糕。虽然它(大多数!)正确地将文本约束到输入大小,但它不会计算正确的高度!它返回的高度基于所提供字体的行高,即使正在进行缩放。我猜这是为什么UILabel默认没有这种行为?我的解决方法是计算高度和宽度都准确的无约束尺寸,然后使用约束宽度和无约束宽度之间的比率来计算约束尺寸的精确高度。什么是PITA。在Apple开发论坛上有很多抱怨,在这里SO指出这个API有很多这样的问题。

答案 1 :(得分: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,字体大小由我的计算确定。

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

答案 2 :(得分:1)

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

如果某人仍然变得奇怪,我不得不改变:

  

intrinsicContentSize = CGSizeMake(inaccurateSize.width,accurateHeight);

  

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

然后它就像魅力一样。

答案 3 :(得分:-3)

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

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

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