UILabelが切り捨てられているかどうかを確認するにはどうすればよいですか?


105

私が持っているUILabel私のアプリは、iPhoneやiPad上の肖像画や風景モードで実行されているかどうかに応じて、長さを変えることができることを。テキストが長すぎて1行で表示できない場合は、ユーザーがそれを押して全文のポップアップを表示できるようにしたいと考えています。

UILabelがテキストを切り捨てているかどうかを確認するにはどうすればよいですか?可能ですか?現在、私は自分のモードに基づいてさまざまな長さをチェックしているだけですが、うまく機能しません。


1
ラインをベースにしたソリューションに見て、私は投稿数えるここに
クラウス

これらすべての年月の後でAppleはまだこれほど基本的なものをUILabelAPIに組み込んでいません。
funct7 2018

回答:


108

文字列幅を計算して、幅が次の値より大きいかどうかを確認できますlabel.bounds.size.width

NSString UIKit Additionsには、特定のフォントで文字列のサイズを計算するためのいくつかのメソッドがあります。ただし、システムがテキストをそのサイズに縮小できるようにするラベルのminimumFontSizeがある場合。その場合は、sizeWithFont:minFontSize:actualFontSize:forWidth:lineBreakMode:を使用できます

CGSize size = [label.text sizeWithAttributes:@{NSFontAttributeName:label.font}];
if (size.width > label.bounds.size.width) {
   ...
}

1
ありがとう、これがまさに私が必要としていたことです。唯一の違いは、sizeWithFont:がCGSizeを返すことでした。
ランドール

ああ、指摘してくれてありがとう、サンプルコードを修正しました。
progrmr 2010年

16
sizeWithFontは非推奨の使用法です:[label.text sizeWithAttributes:@ {NSFontAttributeName:label.font}];
Amelia777 14

2
@fatuhokuはnumberOfLinesで概説されるようにテキストを表示するために使用される最大行数を返すUILabelクラス参照:developer.apple.com/library/ios/documentation/UIKit/Reference/...
ポール

1
ラベルに行数がある場合、次のように幅に行数を掛けてみてくださいif(size.width> label.bounds.size.width * label.numberOfLines){...}
Fadi Abuzant

89

Swift(拡張として)-複数行のuilabelで動作します:

swift4:(attributesパラメータがboundingRect若干変更されました)

extension UILabel {

    var isTruncated: Bool {

        guard let labelText = text else {
            return false
        }

        let labelTextSize = (labelText as NSString).boundingRect(
            with: CGSize(width: frame.size.width, height: .greatestFiniteMagnitude),
            options: .usesLineFragmentOrigin,
            attributes: [.font: font],
            context: nil).size

        return labelTextSize.height > bounds.size.height
    }
}

swift3:

extension UILabel {

    var isTruncated: Bool {

        guard let labelText = text else { 
            return false
        }

        let labelTextSize = (labelText as NSString).boundingRect(
            with: CGSize(width: frame.size.width, height: .greatestFiniteMagnitude),
            options: .usesLineFragmentOrigin,
            attributes: [NSFontAttributeName: font],
            context: nil).size

        return labelTextSize.height > bounds.size.height
    }
}

swift2:

extension UILabel {

    func isTruncated() -> Bool {

        if let string = self.text {

            let size: CGSize = (string as NSString).boundingRectWithSize(
                CGSize(width: self.frame.size.width, height: CGFloat(FLT_MAX)),
                options: NSStringDrawingOptions.UsesLineFragmentOrigin,
                attributes: [NSFontAttributeName: self.font],
                context: nil).size

            if (size.height > self.bounds.size.height) {
                return true
            }
        }

        return false
    }

}

高さ999999.0をCGFloat(FLT_MAX)で置き換えます
アンビエントライト

2
迅速な3の場合、CGFloat.greatestFiniteMagnitudeを使用します
zero3nna

2
良い答えですが、funcの代わりに計算されたプロパティを使用する方が良いです:var isTruncated:Bool {if let string = self.text {let size:CGSize =(string as NSString).boundingRect(with:CGSize(width:self.frame.size .width、height:CGFloat.greatestFiniteMagnitude)、オプション:NSStringDrawingOptions.usesLineFragmentOrigin、attributes:[NSFontAttributeName:self.font]、context:nil).size return(size.height> self.bounds.size.height)} return false}
Mohammadalijf

1
NSAttributedStringを使用していたため、これは私にとっては機能しませんでした。これを機能させるには、使用する属性値を変更する必要がありました:attributedText?.attributes(at:0、effectiveRange:nil)
pulse4life

1
素晴らしい答えです。私の要件に合わせて少し変更する必要がありましたが、完全に機能しました。属性付き文字列も使用していたため、使用した属性は(attributedText?.attributes(at:0、effectiveRange:nil)?? [.font:font])なので、labelTextが空でないかどうかを確認してください。このソリューションを使用します。
Crt Gregoric

20

編集:私は私の答えが賛成されているのを見ました、しかし私が与えたコードスニペットは非推奨です
これを行う最善の方法は(ARC)です。

NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init];
paragraph.lineBreakMode = mylabel.lineBreakMode;
NSDictionary *attributes = @{NSFontAttributeName : mylabel.font,
                             NSParagraphStyleAttributeName : paragraph};
CGSize constrainedSize = CGSizeMake(mylabel.bounds.size.width, NSIntegerMax);
CGRect rect = [mylabel.text boundingRectWithSize:constrainedSize
                                         options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
                                      attributes:attributes context:nil];
if (rect.size.height > mylabel.bounds.size.height) {
    NSLog(@"TOO MUCH");
}

計算されたサイズは整数値ではないことに注意してください。したがって、のようなことを行うとint height = rect.size.height、浮動小数点の精度がいくらか失われ、誤った結果になる可能性があります。

古い回答(非推奨):

ラベルが複数行の場合は、次のコードを使用できます。

CGSize perfectSize = [mylabel.text sizeWithFont:mylabel.font constrainedToSize:CGSizeMake(mylabel.bounds.size.width, NSIntegerMax) lineBreakMode:mylabel.lineBreakMode];
if (perfectSize.height > mylabel.bounds.size.height) {
    NSLog(@"TOO MUCH");
}

13

あなたはUILabelでカテゴリーを作ることができます

- (BOOL)isTextTruncated

{
    CGRect testBounds = self.bounds;
    testBounds.size.height = NSIntegerMax;
    CGRect limitActual = [self textRectForBounds:[self bounds] limitedToNumberOfLines:self.numberOfLines];
    CGRect limitTest = [self textRectForBounds:testBounds limitedToNumberOfLines:self.numberOfLines + 1];
    return limitTest.size.height>limitActual.size.height;
}

3
ドキュメントから:textRectForBounds:limitedToNumberOfLines:「このメソッドを直接呼び出さないでください」...
Martin

@Martinさん、ありがとうございます。実装は制限されています。
ただし

limitedToNumberOfLinesを設定するときに+1するのはなぜですか?
Ash

@Ashを使用して、テキスト用のスペースを広くできる場合にラベルが高いかどうかを確認します。
vrwim 2017年

1
このコードのおかげで、自動レイアウトを使用するときに、いくつかの境界ケースを除いて私にとってはうまくいきました。setNeedsLayout() layoutIfNeeded()メソッドの最初に次を追加して修正しました。
Jovan Jovanovski

9

このカテゴリを使用して、iOS 7以降でラベルが切り捨てられているかどうかを確認します。

// UILabel+Truncation.h
@interface UILabel (Truncation)

@property (nonatomic, readonly) BOOL isTruncated;

@end


// UILabel+Truncation.m
@implementation UILabel (Truncation)

- (BOOL)isTruncated
{
    CGSize sizeOfText =
      [self.text boundingRectWithSize:CGSizeMake(self.bounds.size.width, CGFLOAT_MAX)
                               options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
                            attributes:@{ NSFontAttributeName : label.font } 
                               context: nil].size;

    if (self.frame.size.height < ceilf(sizeOfText.height))
    {
        return YES;
    }
    return NO;
}

@end

9

スウィフト3

文字列を割り当てた後に行数を数え、ラベルの最大行数と比較できます。

import Foundation
import UIKit

extension UILabel {

    func countLabelLines() -> Int {
        // Call self.layoutIfNeeded() if your view is uses auto layout
        let myText = self.text! as NSString
        let attributes = [NSFontAttributeName : self.font]

        let labelSize = myText.boundingRect(with: CGSize(width: self.bounds.width, height: CGFloat.greatestFiniteMagnitude), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: attributes, context: nil)
        return Int(ceil(CGFloat(labelSize.height) / self.font.lineHeight))
    }

    func isTruncated() -> Bool {

        if (self.countLabelLines() > self.numberOfLines) {
            return true
        }
        return false
    }
}

これは迅速な答えです。ありがとう。
JimmyLee

8

iDevの回答に追加するintrinsicContentSizeにはframe、Autolayoutで機能するように、の代わりにを使用する必要があります

- (BOOL)isTruncated:(UILabel *)label{
        CGSize sizeOfText = [label.text boundingRectWithSize: CGSizeMake(label.intrinsicContentSize.width, CGFLOAT_MAX)
                                                     options: (NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
                                                  attributes: [NSDictionary dictionaryWithObject:label.font forKey:NSFontAttributeName] context: nil].size;

        if (self.intrinsicContentSize.height < ceilf(sizeOfText.height)) {
        return YES;
    }
    return NO;
}

ありがとう!UILabelの高さが実際にはテキストに収まるのに十分であるが、行数が限られているため、切り捨てられている場合、フレームではなく組み込みコンテンツサイズを使用することが私の問題の解決策でした。
アントンマトソフ2015年

何らかの理由で、テキストがラベルに収まらない場合でも、NOが返され
ます

6

これだよ。これはで動作しattributedText、プレーンにフォールバックする前にtext、複数のフォントファミリー、サイズ、さらにはNSTextAttachmentsを扱う私たちにとって非常に理にかなっています!

autolayoutでも問題なく機能しますが、チェックする前に制約を定義して設定する必要がありますisTruncated。そうしないと、ラベル自体がどのようにレイアウトするかさえわからないため、切り捨てられているかどうかさえわかりません。

単純なNSStringand だけでこの問題に取り組むことはできませんsizeThatFits。人々がどのようにしてそのような良い結果を得ているのか、私にはわかりません。ところで、何度も言及したように、結果のサイズsizeThatFitsを考慮に入れるため、使用はまったく理想的ではありません。切り詰められているかどうかに関係なく常に返されるnumberOfLinesため、実行しようとしている目的の全体が無効になります。isTruncatedfalse

extension UILabel {
    var isTruncated: Bool {
        layoutIfNeeded()

        let rectBounds = CGSize(width: bounds.width, height: .greatestFiniteMagnitude)
        var fullTextHeight: CGFloat?

        if attributedText != nil {
            fullTextHeight = attributedText?.boundingRect(with: rectBounds, options: .usesLineFragmentOrigin, context: nil).size.height
        } else {
            fullTextHeight = text?.boundingRect(with: rectBounds, options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font], context: nil).size.height
        }

        return (fullTextHeight ?? 0) > bounds.size.height
    }
}

これは私のおかげで正常に動作します!!これに対する更新または変更はありますか?
amodkanthe

すばらしい答えです。唯一の問題は、attributedTextが設定されていなくても、nilになることはありません。したがって、これを2つの変数isTextTruncatedとisAttributedTextTruncatedとして実装しました。
ハリス

@Harrisは、uilabel.hファイルでデフォルトがnilであると言います
Lucas

@LucasChwe知っていますが、実際にはそうではありません。自分で試してみてください。fyi、forums.developer.apple.com
Harris

3

これは、Swift 3で選択された回答です(拡張機能として)。OPは約1行のラベルを求めていました。ここで私が試した迅速な回答の多くは複数行のラベルに固有であり、単一行のラベルに正しくフラグを立てていません。

extension UILabel {
    var isTruncated: Bool {
        guard let labelText = text as? NSString else {
            return false
        }
        let size = labelText.size(attributes: [NSFontAttributeName: font])
        return size.width > self.bounds.width
    }
}

アクセルの答えはこれではうまくいきませんでした。これはしました。ありがとう!
グレン

2

これはiOS 8で機能します。

CGSize size = [label.text boundingRectWithSize:CGSizeMake(label.bounds.size.width, NSIntegerMax) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : label.font} context:nil].size;

if (size.height > label.frame.size.height) {
    NSLog(@"truncated");
}

2

UILabelのトランケーションを処理するためのカテゴリを作成しました。iOS 7以降で動作します。それが役に立てば幸い ! uilabelの尾の切り捨て

@implementation UILabel (Truncation)

- (NSRange)truncatedRange
{
    NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:[self attributedText]];

    NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
    [textStorage addLayoutManager:layoutManager];

    NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:[self bounds].size];
    textContainer.lineFragmentPadding = 0;
    [layoutManager addTextContainer:textContainer];

    NSRange truncatedrange = [layoutManager truncatedGlyphRangeInLineFragmentForGlyphAtIndex:0];
    return truncatedrange;
}

- (BOOL)isTruncated
{
    return [self truncatedRange].location != NSNotFound;
}

- (NSString *)truncatedText
{
    NSRange truncatedrange = [self truncatedRange];
    if (truncatedrange.location != NSNotFound)
    {
        return [self.text substringWithRange:truncatedrange];
    }

    return nil;
}

@end

NSNotFoundのみが提供されるたび
Dari

2
extension UILabel {

public func resizeIfNeeded() -> CGFloat? {
    guard let text = text, !text.isEmpty else { return nil }

    if isTruncated() {
        numberOfLines = 0
        sizeToFit()
        return frame.height
    }
    return nil
}

func isTruncated() -> Bool {
    guard let text = text, !text.isEmpty else { return false }

    let size: CGSize = text.size(withAttributes: [NSAttributedStringKey.font: font])
    return size.width > self.bounds.size.width
    }
}

文字列の幅を計算して、その幅がラベルの幅よりも大きいかどうかを確認できます。


1

@iDevの機能に追加するために、self.frame.size.heightを使用するように変更し、label.frame.size.heightも使用しないようにしましたNSStringDrawingUsesLineFontLeading。これらの変更後、切り捨てがいつ発生するかを完全に計算しました(少なくとも私の場合)。

- (BOOL)isTruncated:(UILabel *)label {
    CGSize sizeOfText = [label.text boundingRectWithSize: CGSizeMake(label.bounds.size.width, CGFLOAT_MAX)
                                                 options: (NSStringDrawingUsesLineFragmentOrigin)
                                              attributes: [NSDictionary dictionaryWithObject:label.font forKey:NSFontAttributeName] context: nil].size;

    if (label.frame.size.height < ceilf(sizeOfText.height)) {
        return YES;
    }
    return NO;
}

1

boundingRect(with:options:attributes:context:)自動レイアウト(最大の高さを設定する)と属性付きテキストを使用するときに問題がありましたNSParagraph.lineSpacing

行間のスペースは無視されたため(メソッドに渡されattributesた場合でもboundingRect)、ラベルは切り捨てられなかったと見なされる場合があります。

私が見つけた解決策は使用することUIView.sizeThatFitsです:

extension UILabel {
    var isTruncated: Bool {
        layoutIfNeeded()
        let heightThatFits = sizeThatFits(bounds.size).height
        return heightThatFits > bounds.size.height
    }
}

0

上記のすべての回答は減価償却された方法を使用しているため、これは役立つと思いました:

- (BOOL)isLabelTruncated:(UILabel *)label
{
    BOOL isTruncated = NO;

    CGRect labelSize = [label.text boundingRectWithSize:CGSizeFromString(label.text) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName : label.font} context:nil];

    if (labelSize.size.width / labelSize.size.height > label.numberOfLines) {

        isTruncated = YES;
    }

    return isTruncated;
}

幅を高さで除算していて、それが行数(0の場合が非常に多い)より大きい場合は、ラベルが切り捨てられていると言います。これが機能する方法はありません。
CanLeloğlu2015年

@CanLeloğluをテストしてください。実例です。
Raz

2
numberOfLinesがゼロに等しい場合はどうなりますか?
CanLeloğlu2015年

0

iOS 6を処理するために(はい、私たちの一部はまだそうしなければなりません)、@ iDevの答えに対するもう1つの拡張があります。重要なポイントは、iOS 6の場合、sizeThatFitsを呼び出す前に、UILabelのnumberOfLinesが0に設定されていることを確認することです。そうでない場合は、ラベルのテキストを描画するために「高さのnumberOfLinesを描画するためのポイント」が必要であるという結果が得られます。

- (BOOL)isTruncated
{
    CGSize sizeOfText;

    // iOS 7 & 8
    if([self.text respondsToSelector:@selector(boundingRectWithSize:options:attributes:context:)])
    {
        sizeOfText = [self.text boundingRectWithSize:CGSizeMake(self.bounds.size.width,CGFLOAT_MAX)
                                             options:(NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading)
                                          attributes:@{NSFontAttributeName:self.font}
                                             context:nil].size;
    }
    // iOS 6
    else
    {
        // For iOS6, set numberOfLines to 0 (i.e. draw label text using as many lines as it takes)
        //  so that siteThatFits works correctly. If we leave it = 1 (for example), it'll come
        //  back telling us that we only need 1 line!
        NSInteger origNumLines = self.numberOfLines;
        self.numberOfLines = 0;
        sizeOfText = [self sizeThatFits:CGSizeMake(self.bounds.size.width,CGFLOAT_MAX)];
        self.numberOfLines = origNumLines;
    }

    return ((self.bounds.size.height < sizeOfText.height) ? YES : NO);
}

0

これらのいずれかを必ずviewDidLayoutSubviewsで呼び出してください。

public extension UILabel {

    var isTextTruncated: Bool {
        layoutIfNeeded()
        return text?.boundingRect(with: CGSize(width: bounds.width, height: .greatestFiniteMagnitude), options: .usesLineFragmentOrigin, attributes: [NSAttributedString.Key.font: font!], context: nil).size.height ?? 0 > bounds.size.height
    }

    var isAttributedTextTruncated: Bool {
        layoutIfNeeded()
        return attributedText?.boundingRect(with: CGSize(width: bounds.width, height: .greatestFiniteMagnitude), options: .usesLineFragmentOrigin, context: nil).size.height ?? 0 > bounds.size.height
    }

}

0

SWIFT 5

3行のみを表示するように設定されている複数行のUILabelの例。

    let labelSize: CGSize = myLabel.text!.size(withAttributes: [.font: UIFont.systemFont(ofSize: 14, weight: .regular)])

    if labelSize.width > myLabel.intrinsicContentSize.width * 3 {
        // your label will truncate
    }

ユーザーは、「テキスト幅」に追加せずに行を追加してReturnキーを選択することもできますが、その場合、このようなものも役立ちます。

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {

    if text == "\n" {
        // return pressed 

    }
}

-1

Swift 3ソリューション

最良の解決策は、(1)UILabel切り捨てをチェックしているラベルと同じプロパティでを作成すること、(2)呼び出し.sizeToFit()(3)ダミーラベルの属性を実際のラベルと比較することです。

たとえば、幅が変化する1行のラベルが切り捨てられるかどうかを確認する場合は、次の拡張機能を使用できます。

extension UILabel {
    func isTruncated() -> Bool {
        let label = UILabel(frame: CGRect(x: 0, y: 0, width: CGFloat.greatestFiniteMagnitude, height: self.bounds.height))
        label.numberOfLines = 1
        label.font = self.font
        label.text = self.text
        label.sizeToFit()
        if label.frame.width > self.frame.width {
            return true
        } else {
            return false
        }
    }
}

...しかし、繰り返しになりますが、必要に応じて上記のコードを簡単に変更できます。したがって、ラベルが複数行で高さが異なるとしましょう。次に、拡張機能は次のようになります。

extension UILabel {
    func isTruncated() -> Bool {
        let label = UILabel(frame: CGRect(x: 0, y: 0, width: self.bounds.width, height: CGFloat.greatestFiniteMagnitude))
        label.numberOfLines = 0
        label.font = self.font
        label.text = self.text
        label.sizeToFit()
        if label.frame.height > self.frame.height {
            return true
        } else {
            return false
        }
    }
}

-6

labelのtitle属性を設定するのは簡単ではありません。これを設定すると、ホバーしたときに完全なラベルが表示されます。

ラベルの長さとdivの幅を計算できます(長さに変換-jQuery / Javascript-ピクセル値(20px)を数値(20)に変換するにはどうすればよいですか)。

長さがdiv幅より大きい場合にjqueryを設定してタイトルを設定します。

var divlen = parseInt(jQuery("#yourdivid").width,10);
var lablen =jQuery("#yourlabelid").text().length;
if(lablen < divlen){
jQuery("#yourlabelid").attr("title",jQuery("#yourlabelid").text());
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.