Результирующие строки UILabel с UILineBreakModeWordWrap - PullRequest
18 голосов
/ 02 ноября 2010

У меня есть UILabel, размер которого рассчитывается методом sizeWithFont:.Режим разрыва строки установлен на UILineBreakModeWordWrap (тот же флаг используется при расчете размера с помощью sizeWithFont:) ...

Все отлично работает, метка имеет правильный размер и отображает мой текст по мере необходимости.

Теперь мне нужно знать строки, которые используются для отображения метки (или строки, которые генерируются при использовании sizeWithFont:).Технически я мог бы написать свою собственную реализацию переноса строк, основанную на возвратах пробелов / кареток, но тогда это не будет гарантировано так же, как реализация Apple, и, следовательно, результирующие строки не будут теми, которые используются для вычисления размера текста.не говоря уже о том, чтобы заново изобретать колесо.

В идеале я бы пропустил свою строку, указал режим ширины и разрыва строки и получил бы массив строк, представляющих визуальные строки текста.

Любойидеи, как сделать это самым элегантным образом?

Ответы [ 5 ]

38 голосов
/ 13 ноября 2010

Чтобы рассчитать количество строк, которое UILabel имеет после переноса текста, вам нужно найти начальную (высоту строки) шрифта UILabel (label.font.leading) и затем разделить высоту многострочный UILabel по высоте каждой строки, чтобы получить количество строк.

Вот пример:

- (void)viewDidLoad {

    [super viewDidLoad];

    UILabel *label = [[[UILabel alloc] initWithFrame:CGRectZero] autorelease];
    label.numberOfLines = 0;
    label.lineBreakMode = UILineBreakModeWordWrap;  
    label.text = @"Some really really long string that will cause the label's text to wrap and wrap and wrap around. Some really really long string that will cause the label's text to wrap and wrap and wrap around.";

    CGRect frame = label.frame;
    frame.size.width = 150.0f;
    frame.size = [label sizeThatFits:frame.size];
    label.frame = frame;

    CGFloat lineHeight = label.font.leading;
    NSUInteger linesInLabel = floor(frame.size.height/lineHeight);
    NSLog(@"Number of lines in label: %i", linesInLabel);

    [self.view addSubview:label];

}

Или вы можете сделать это в две строки:

[label sizeToFit];
int numLines = (int)(label.frame.size.height/label.font.leading);
24 голосов
/ 09 ноября 2010

Не думаю, что для этого есть серебряная пуля.

Вот метод категории, который, кажется, работает для нескольких базовых тестовых случаев, которые я бросил в него. Нет гарантий, что это не сломается с чем-то сложным!

Способ, которым это работает, состоит в том, чтобы пройти проверку строки, чтобы увидеть, подходит ли диапазон слов ширине метки. Когда он вычисляет, что текущий диапазон слишком широк, он записывает последний подходящий диапазон в виде строки.

Я не утверждаю, что это эффективно. Лучшим способом может быть реализация собственной UILabel ...

@interface UILabel (Extensions)

- (NSArray*) lines;

@end

@implementation UILabel (Extensions)

- (NSArray*) lines
{
    if ( self.lineBreakMode != UILineBreakModeWordWrap )
    {
        return nil;
    }

    NSMutableArray* lines = [NSMutableArray arrayWithCapacity:10];

    NSCharacterSet* wordSeparators = [NSCharacterSet whitespaceAndNewlineCharacterSet];

    NSString* currentLine = self.text;
    int textLength = [self.text length];

    NSRange rCurrentLine = NSMakeRange(0, textLength);
    NSRange rWhitespace = NSMakeRange(0,0);
    NSRange rRemainingText = NSMakeRange(0, textLength);
    BOOL done = NO;
    while ( !done )
    {
        // determine the next whitespace word separator position
        rWhitespace.location = rWhitespace.location + rWhitespace.length;
        rWhitespace.length = textLength - rWhitespace.location;
        rWhitespace = [self.text rangeOfCharacterFromSet: wordSeparators options: NSCaseInsensitiveSearch range: rWhitespace];
        if ( rWhitespace.location == NSNotFound )
        {
            rWhitespace.location = textLength;
            done = YES;
        }

        NSRange rTest = NSMakeRange(rRemainingText.location, rWhitespace.location-rRemainingText.location);

        NSString* textTest = [self.text substringWithRange: rTest];

        CGSize sizeTest = [textTest sizeWithFont: self.font forWidth: 1024.0 lineBreakMode: UILineBreakModeWordWrap];
        if ( sizeTest.width > self.bounds.size.width )
        {
            [lines addObject: [currentLine stringByTrimmingCharactersInSet:wordSeparators]];
            rRemainingText.location = rCurrentLine.location + rCurrentLine.length;
            rRemainingText.length = textLength-rRemainingText.location;
            continue;
        }

        rCurrentLine = rTest;
        currentLine = textTest;
    }

    [lines addObject: [currentLine stringByTrimmingCharactersInSet:wordSeparators]];

    return lines;
}

@end

используйте так:

NSArray* lines = [_theLabel lines];

int count = [lines count];
11 голосов
/ 18 января 2013

Просто вызовите метод ниже и передайте UILabel или UITextView:

-(NSInteger)getNumberOfLinesInLabelOrTextView:(id)obj
{
    NSInteger lineCount = 0;
    if([obj isKindOfClass:[UILabel class]])
    {
        UILabel *label = (UILabel *)obj;

       // This method is deprecated in iOS 7.0 or later 
       // CGSize requiredSize = [label.text sizeWithFont:label.font constrainedToSize:label.frame.size lineBreakMode:label.lineBreakMode]; 

        CGSize requiredSize = [label.text boundingRectWithSize:CGSizeMake(CGRectGetWidth(label.frame), CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:label.font} context:nil].size;

        int charSize = label.font.leading;
        int rHeight = requiredSize.height;

        lineCount = rHeight/charSize;
    }
    else if ([obj isKindOfClass:[UITextView class]])
    {
        UITextView *textView = (UITextView *)obj;
        lineCount = textView.contentSize.height / textView.font.leading;
    }

    return lineCount;
}

Теперь вызовите этот метод: -

NSLog(@"%d",[self getNumberOfLinesInLabelOrTextView:label]);
NSLog(@"%d",[self getNumberOfLinesInLabelOrTextView:textView]);

ОБНОВЛЕНО: SWIFT-КОД

func getNumberOfLinesInLabelOrTextView(obj:AnyObject) -> NSInteger {

    var lineCount: NSInteger = 0
    if (obj.isKindOfClass(UILabel)) {

        let label: UILabel = obj as! UILabel
        let requiredSize: CGSize = (label.text)!.boundingRectWithSize(CGSizeMake(CGRectGetWidth(label.frame), CGFloat.max), options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: [NSFontAttributeName: label.font], context: nil).size
        let charSize: CGFloat = label.font.leading
        let rHeight: CGFloat = requiredSize.height
        lineCount = (NSInteger)(rHeight/charSize)
    }
    else if (obj.isKindOfClass(UITextView)){

        let textView: UITextView = obj as! UITextView
        lineCount = (NSInteger)(textView.contentSize.height / textView.font.leading)
    }

    return lineCount
}

Теперь вызовите этот метод: -

println("%d \(self.getNumberOfLinesInLabelOrTextView(textView))")
println("%d \(self.getNumberOfLinesInLabelOrTextView(label))")

Примечание: leading - использовать lineHeight. не возвращает фактическое ведение. будет официально объявлен устаревшим в будущем.

0 голосов
/ 19 января 2017

Обновлено для Swift 3

Чтобы вычислить количество строк, которые есть у UILabel, после переноса текста вам нужно разделить высоту вашей многострочной UILabel на высоту каждой строки (ascender).

При попытке ответа Адольфо по какой-то причине label.font.leading вернул 0.0, поэтому я использовал label.font.ascender, который возвращает высоту от базовой линии до вершины кадра UILabel. Смотри картинку ниже.

enter image description here

//Makes label go to another line if necessary
label.numberOfLines = 0 //Set num of lines to infinity
label.lineBreakMode = .byWordWrapping
label.sizeToFit()
let numLines = Int(label.frame.size.height/label.font.ascender)
0 голосов
/ 20 мая 2016

для Xcode 7 и выше Для ответа TheTiger требуется обновление, прокомментированное с кодом ниже:

-(NSInteger)getNumberOfLinesInLabelOrTextView:(id)obj
{
    NSInteger lineCount = 0;
    if([obj isKindOfClass:[UILabel class]])
    {
        UILabel *label = (UILabel *)obj;
        CGSize requiredSize = [label.text boundingRectWithSize:CGSizeMake(CGRectGetWidth(label.frame), CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:label.font} context:nil].size;

        int charSize = label.font.leading;
        // now listen , you need to set the text or label with only 1 
        // then nslog(@"%d",charSize);
        // then change the line int charSize = label.font.leading; into 
        // int charSize = the printed value in case of 1 line
        int rHeight = requiredSize.height;

        lineCount = rHeight/charSize;
    }
    else if ([obj isKindOfClass:[UITextView class]])
    {
        UITextView *textView = (UITextView *)obj;
        lineCount = textView.contentSize.height / textView.font.leading;
    }

    return lineCount;
}

это будет работать, только если вы используете тот же шрифт и размер, это не разумный шаг, но он помог мне, и я хотел бы поделиться им как текущим решением, которое я знаю

...