web-dev-qa-db-ja.com

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

IPhoneまたはiPadでアプリがポートレートモードまたはランドスケープモードで実行されているかどうかに応じて長さが異なるUILabelがあります。テキストが長すぎて1行に表示できない場合、ユーザーがそれを押して全文のポップアップを表示できるようにします。

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

92
Randall

文字列の幅 を計算し、幅が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) {
   ...
}
102
progrmr

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

Swift4:(attributesboundingRect paramはわずかに変更されました)

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
    }

}
77
Robin

編集:私はちょうど私の答えが支持されたのを見ましたが、私が与えたコードスニペットは非推奨です。
これを行う最良の方法は(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");
}
19
Martin

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

このカテゴリを使用して、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
Rajesh

iDev の答えに追加するには、intrinsicContentSizeの代わりにframeを使用して、自動レイアウトで機能するようにする必要があります。

- (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;
}
8
onmyway133

Swift

文字列を割り当てた後、行数をカウントし、ラベルの最大行数と比較できます。

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

これです。これはattributedTextで機能しますが、それからプレーンtextにフォールバックします。これは、複数のフォントファミリ、サイズ、さらにはNSTextAttachmentsを扱う人々にとって非常に理にかなっています。

自動レイアウトでは問題なく動作しますが、明らかにisTruncatedをチェックする前に制約を定義および設定する必要があります。そうしないと、ラベル自体がレイアウト方法を認識できなくなるため、切り捨てられたかどうかさえわかりません。

単純なNSStringsizeThatFitsだけでこの問題にアプローチすることはできません。人々がそのような肯定的な結果をどのように得ていたかはわかりません。ところで、何度も述べたように、sizeThatFitsを使用することは、結果のサイズに対してnumberOfLinesを考慮するため、まったく理想的ではありません。これは、isTruncatedは、切り捨てられているかどうかに関係なく、常にfalseを返します。

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
    }
}
3
Lucas Chwe

UILabelの切り捨てを処理するためのカテゴリを作成しました。 iOS 7以降で動作します。それが役に立てば幸い ! ilabel末尾の切り捨て

@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
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
Marcio Fonseca

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

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

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

extension UILabel {
    var isTruncated: Bool {
        layoutIfNeeded()
        let heightThatFits = sizeThatFits(bounds.size).height
        return heightThatFits > bounds.size.height
    }
}
1
Axel Guilmin
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
Hayk Brsoyan

Swift 3(拡張子として)で選択された回答です。OPは約1行のラベルを求めていました。ここで試した多くのSwift複数行のラベルに、単一行のラベルに正しくフラグが付けられていません。

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
    }
}
1
Travis M.

@ 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
kgaidis

上記の答えはすべて減価償却方法を使用しているため、これは便利だと思いました。

- (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
Raz

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
John Jacecko