24

如何在 UILabel 中对齐不同大小的文本?一个例子是在价格横幅中将较小的美分金额与较大的美元金额对齐。

示例图像

iOS6 中的 UILabel 支持NSAttributedString允许我在同一个 UILabel 中拥有不同大小的文本。但是,它似乎没有顶部对齐文本的属性。有哪些实施方案?在我看来,提供自定义绘图逻辑以基于自定义属性字符串键进行顶部对齐可能是最好的,但我不知道如何去做。

4

4 回答 4

56

我能够使用单个标签达到您想要的结果。

使用一点数学,您可以偏移较小文本的基线以达到您想要的结果。

Objective-C

- (NSMutableAttributedString *)styleSalePriceLabel:(NSString *)salePrice withFont:(UIFont *)font
{
    if ([salePrice rangeOfString:@"."].location == NSNotFound) {
        return [[NSMutableAttributedString alloc] initWithString:salePrice];
    } else {
        NSRange range = [salePrice rangeOfString:@"."];
        range.length = (salePrice.length - range.location);
        NSMutableAttributedString *stylizedPriceLabel = [[NSMutableAttributedString alloc] initWithString:salePrice];
        UIFont *smallFont = [UIFont fontWithName:font.fontName size:(font.pointSize / 2)];
        NSNumber *offsetAmount = @(font.capHeight - smallFont.capHeight);
        [stylizedPriceLabel addAttribute:NSFontAttributeName value:smallFont range:range];
        [stylizedPriceLabel addAttribute:NSBaselineOffsetAttributeName value:offsetAmount range:range];
        return stylizedPriceLabel;
    }
}

迅速

extension Range where Bound == String.Index {
    func asNSRange() -> NSRange {
        let location = self.lowerBound.encodedOffset
        let length = self.lowerBound.encodedOffset - self.upperBound.encodedOffset
        return NSRange(location: location, length: length)
    }
}

extension String {
    func asStylizedPrice(using font: UIFont) -> NSMutableAttributedString {
        let stylizedPrice = NSMutableAttributedString(string: self, attributes: [.font: font])

        guard var changeRange = self.range(of: ".")?.asNSRange() else {
            return stylizedPrice
        }

        changeRange.length = self.count - changeRange.location
        // forgive the force unwrapping
        let changeFont = UIFont(name: font.fontName, size: (font.pointSize / 2))!
        let offset = font.capHeight - changeFont.capHeight
        stylizedPrice.addAttribute(.font, value: changeFont, range: changeRange)
        stylizedPrice.addAttribute(.baselineOffset, value: offset, range: changeRange)
        return stylizedPrice
    }
}

这会产生以下结果:

于 2014-08-26T23:23:11.220 回答
10

尝试简单地通过对齐框架的原点来做到这一点的问题是,“正常”字符通常最终会在它们周围加上一些额外的填充,因为标签必须容纳所有字体的字符,包括那些将上升的字符和长的下降字符。您会在您发布的图片中注意到,如果较小的“99”是一个单独的标签,并且设置为与较大的文本相同的来源,那么由于美元符号的最高点,它会太高。

幸运的是,UIFont它为我们提供了正确执行此操作所需的所有信息。我们需要测量标签正在使用的空上升空间并调整相对位置以解决它,如下所示:

//Make sure the labels hug their contents
[self.bigTextLabel sizeToFit];
[self.smallTextLabel sizeToFit];

//Figure out the "blank" space above normal character height for the big text
UIFont *bigFont = self.bigTextLabel.font;
CGFloat bigAscenderSpace = (bigFont.ascender - bigFont.capHeight);

//Move the small text down by that ammount
CGFloat smallTextOrigin = CGRectGetMinY(self.bigTextLabel.frame) + bigAscenderSpace;

//Figure out the "blank" space above normal character height for the little text
UIFont *smallFont = self.smallTextLabel.font;
CGFloat smallAscenderSpace = smallFont.ascender - smallFont.capHeight;

//Move the small text back up by that ammount
smallTextOrigin -= smallAscenderSpace;

//Actually assign the frames
CGRect smallTextFrame = self.smallTextLabel.frame;
smallTextFrame.origin.y = smallTextOrigin;
self.smallTextLabel.frame = smallTextFrame;

(此代码假设您有两个标签属性,分别名为bigTextLabelsmallTextLabel


编辑:

在没有两个标签的情况下执行此操作非常相似。您可以创建一个自定义 UIView 子类并NSAttributedStrings使用该-drawInRect:options:context:方法在其中绘制(确保NSStringDrawingUsesLineFragmentOrigin在您的选项中使用)。计算顶部对齐的数学应该是相同的,唯一的区别是您通过NSFontAttributeName属性而不是标签从属性字符串中获取字体。2012 WWDC 关于属性字符串绘制的视频是一个很好的参考。

于 2013-03-21T04:22:52.483 回答
2

这太糟糕了。在稍作修改后,我没有足够的声誉来评论我使用得很好的答案。我只想指出使用smallfontcents 而不是font,这可能是一个错字。

下面是修改后的代码

- (NSMutableAttributedString *)styleSalePriceLabel:(NSString *)salePrice withFont:(UIFont *)font
{
    if ([salePrice rangeOfString:@"."].location == NSNotFound) {
        return [[NSMutableAttributedString alloc]  
                   initWithString:salePrice];
    } else {
        NSRange range = [salePrice rangeOfString:@"."];
        range.length = (salePrice.length - range.location);
        NSMutableAttributedString *stylizedPriceLabel =
            [[NSMutableAttributedString alloc] initWithString:salePrice];
        UIFont *smallfont = [UIFont fontWithName:font.fontName 
                                            size:(font.pointSize / 2)];
        NSNumber *offsetAmount = @(font.capHeight - smallfont.capHeight);
        [stylizedPriceLabel addAttribute:NSFontAttributeName 
                                   value:smallfont 
                                   range:range];
        [stylizedPriceLabel addAttribute:NSBaselineOffsetAttributeName 
                                   value:offsetAmount 
                                   range:range];
        return stylizedPriceLabel;
    }
}
于 2015-10-13T20:38:12.393 回答
0

一种可能的方法是使标签与您输入的文本的大小完全相同。

CGRect labelFrame;
//Set the origin of the label to whatever you want
labelFrame.size = [label.text sizeWithFont:label.font];  //If using NSString
labelFrame.size = [label.text size];                     //If using NSAttributedString
label.frame = labelFrame;

有关更多详细信息,请参阅此类似的 SO 帖子

于 2013-03-21T00:54:46.013 回答