15

したがって、このテキスト ラベルが制約を介してサムネイルの上部に揃えられるように、IB でビューを設定しています。

ここに画像の説明を入力

ただし、ご存知のように、UILabel でテキストを垂直方向に揃えることはできません。テキストは、コンテンツの長さに基づいてフォント サイズを更新します。フルサイズのテキストは見栄えがよく、小さなテキストはビュー上でかなり低くなります。

ここに画像の説明を入力

既存のソリューションでは、sizeToFit を呼び出すか、テキストの高さに合わせて uilabel のフレームを更新します。残念ながら、後者の (醜い) ソリューションは、フレームを更新することが想定されていない制約ではうまく機能しません。テキストが切り捨てられるまで自動縮小する必要がある場合、前者のソリューションは基本的に機能しません。(したがって、制限された数の行と自動縮小では機能しません)。

「コンテンツに合わせてサイズ」を介して自然なサイズに設定されている場合、幅のように uilabel の固有のサイズ (高さ) が更新されない理由については、私には理解できません。それは間違いないように思えますが、そうではありません。

そのため、代替ソリューションを探しています。私が見る限り、ラベルに高さの制約を設定し、テキストの高さを計算した後に高さ定数を調整する必要があるかもしれません。誰でも良い解決策がありますか?

4

4 に答える 4

7

この問題は、解決すべき真の PITA です。動作する API が iOS7 で非推奨になったり、iOS7 の代替 API が壊れたりすることは役に立ちません。何とか!

あなたのソリューションは素晴らしいですが、非推奨の API ( sizeWithFont:minFontSize:actualFontSize:forWidth:lineBreakMode:) を使用しており、あまりカプセル化されていません。この動作が必要なセルまたはビューにこのコードをコピーする必要があります。プラス面では、かなり効率的です!1 つのバグは、計算を行うときにラベルがまだレイアウトされていないことですが、幅に基づいて計算を実行することです。

この動作を 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 dev フォーラムとここ 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 (不正確なサイズ.幅、正確な高さ);

本質的なコンテンツ サイズ = CGSizeMake (不正確なサイズ.幅、正確な高さ * 2);

それは魅力のように機能しました。

于 2015-09-07T20:33:40.400 に答える
-3

探しているのは、この 2 行のコードです。

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

これは、「改行」と「行」の下の属性インスペクターにもあります。

于 2013-03-19T21:37:18.463 に答える