Как проверить, урезан ли UILabel? - PullRequest
88 голосов
/ 20 июня 2010

У меня есть UILabel, который может иметь различную длину в зависимости от того, работает ли мое приложение в портретном или альбомном режиме на iPhone или iPad. Когда текст слишком длинный, чтобы отображаться в одной строке, и он усекается, я хочу, чтобы пользователь мог нажать на него и получить всплывающее окно с полным текстом.

Как проверить, усекает ли UILabel текст? Это вообще возможно? Прямо сейчас я просто проверяю разные длины в зависимости от того, в каком режиме я нахожусь, но он работает не очень хорошо.

Ответы [ 18 ]

98 голосов
/ 20 июня 2010

Вы можете вычислить ширину строки и посмотреть, превышает ли ширина label.bounds.size.width

NSString UIKit Дополнения имеет несколько методов для вычисления размера строки с определенным шрифтом. Однако, если у вас есть минимальный размер шрифта для вашей метки, который позволяет системе сжимать текст до этого размера. Вы можете использовать sizeWithFont: minFontSize: actualFontSize: forWidth: lineBreakMode: в этом случае.

CGSize size = [label.text sizeWithAttributes:@{NSFontAttributeName:label.font}];
if (size.width > label.bounds.size.width) {
   ...
}
74 голосов
/ 16 июля 2015

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
    }

}
19 голосов
/ 07 ноября 2012

РЕДАКТИРОВАТЬ: Я только что увидел, что мой ответ был отклонен, но фрагмент кода, который я дал, устарел.
Теперь лучший способ сделать это (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");
}
12 голосов
/ 26 октября 2012

вы можете сделать категорию с 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;
}
9 голосов
/ 01 июля 2014

Используйте эту категорию, чтобы узнать, урезан ли ярлык на 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
8 голосов
/ 22 октября 2014

Чтобы добавить к 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;
}
7 голосов
/ 24 марта 2017

Swift 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
    }
}
2 голосов
/ 17 декабря 2014

Это работает для 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 голосов
/ 31 октября 2018

Это мальчики. Это работает с attributedText, прежде чем вернуться к обычному text, что имеет большой смысл для нас, людей, которые имеют дело с несколькими семействами шрифтов, размерами и даже NSTextAttachments!

Прекрасно работает с autolayout, но, очевидно, ограничения должны быть определены и установлены до того, как мы проверим isTruncated, в противном случае сам ярлык даже не будет знать, как его размещать, поэтому он даже не узнает, урезан ли он. *

Это не работает для решения этой проблемы с простыми NSString и sizeThatFits. Я не уверен, как люди получали такие положительные результаты. Кстати, как уже неоднократно упоминалось, использование 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
    }
}
2 голосов
/ 13 июня 2015

Я написал категорию для работы с усечением 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
...