web-dev-qa-db-ja.com

UILabel内のさまざまなサイズのテキストを上揃え

UILabel内で異なるサイズのテキストを上揃えにする方法は?例としては、価格バナーで小さいサイズのセント金額と大きいサイズのドル金額を上揃えすることがあります。

Sample Image

IOS6のUILabelはNSAttributedStringをサポートしています。これにより、同じUILabelに異なるサイズのテキストを含めることができます。ただし、テキストを上揃えにするための属性がないようです。これを実装するためのオプションは何ですか?カスタム属性の文字列キーに基づいて上揃えを行うためのカスタム描画ロジックを提供するのが最善のように思えますが、どうすればよいかわかりません。

23
ejel

ひとつのラベルで思い通りの結果が得られました。

少し計算を使用して、小さいテキストのベースラインをオフセットして、目的の結果を得ることができます。

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;
    }
}

Swift

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
    }
}

これにより、次のようになります。

49
Eric Murphey

フレームの原点を揃えるだけでこれを実行しようとすると、ラベルがフォントの文字のallに対応する必要があるため、通常、「通常の」文字の周囲に余分なパディングが発生するという問題があります。背の高いアセンダーと長いディセンダー。投稿した画像で、小さい「99」が大きいテキストと同じOriginに設定された別のラベルである場合、ドル記号の最上部のために高すぎることに気付くでしょう。

幸い、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;

(このコードは、それぞれbigTextLabelおよびsmallTextLabelという名前の2つのラベルプロパティがあることを前提としています)


編集:

2つのラベルなしでこれを行うことはかなり似ています。カスタムUIViewサブクラスを作成し、その中に-drawInRect:options:context:メソッドを使用してNSAttributedStringsを描画できます(オプションでは必ずNSStringDrawingUsesLineFragmentOriginを使用してください)。上部の配置を計算するための計算は同じである必要がありますが、唯一の違いは、ラベルではなく、NSFontAttributeName属性を介して属性付き文字列からフォントを取得することです。属性付き文字列の描画に関する2012年のWWDCビデオは、このための良いリファレンスです。

10
user1059264

それは残念です。少し変更を加えた後、よく使った答えをコメントするのに十分な評判がありません。 smallfontの代わりに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;
    }
}
2
Corey

考えられるアプローチの1つは、ラベルを入力するテキストの正確なサイズにすることです。

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詳細については投稿

0