この問題は、解決すべき真の 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:context
APIはかなり壊れているようです。それは(ほとんど!)テキストを入力サイズに正しく制限しますが、正しい高さを計算しません! 返される高さは、スケーリングが行われている場合でも、提供されたフォントの行の高さに基づいています。私の推測では、これがUILabel
デフォルトでこの動作を持たない理由ですか? 私の回避策は、高さと幅の両方が正確な制約のないサイズを計算し、制約のある幅と制約のない幅の比率を使用して、制約のあるサイズの正確な高さを計算することです。なんというピタ。Apple dev フォーラムとここ SO には、この API にこのような多くの問題があることを指摘する多くの苦情があります。