Вернуть NSString с переносом слов из более длинного NSString

Возможный дубликат:
UITextView: получить текст с информацией о переносе

Я исследовал библиотеку NSString и многочисленные библиотеки на предмет функции, которая может принимать длинную строку вроде этой:

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra. Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi. Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci, sagittis tempus lacus enim ac dui.

и вместе с CGSize или float, указывающими ширину и используемый шрифт, и верните мне строку с \ n разрывами и обернутыми словами.

Результат (примерно):

Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac\n
egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet.\n
 Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. \n
placerat eleifend leo. Quisque sit amet est et sapien ullamcorper pharetra.\n
 Vestibulum erat wisi, condimentum sed, commodo vitae, ornare sit amet, wisi.\n
 Aenean fermentum, elit eget tincidunt condimentum, eros ipsum rutrum orci,\n
 sagittis tempus lacus enim ac dui. 

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

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

Я полагаю, что это близко к [NSString sizeWithFont: forWidth: lineBreakMode:], но мне не нужен размер, мне нужна сама строка.


person Nils Munch    schedule 27.11.2012    source источник
comment
Думаю, вам придется что-то написать самостоятельно, используя уже упомянутый вызов sizeWithFont.   -  person jimpic    schedule 27.11.2012
comment
@cem, хотя вопрос не тот же, решение достаточно близко, чтобы я мог сделать функцию, чтобы удовлетворить мою проблему.   -  person Nils Munch    schedule 27.11.2012
comment
Какой API вы используете для рисования строки? Если вы визуализировали каждый символ в текстуре (которая может ужасно сломаться для большого количества текста, отличного от ASCII), тогда просто используйте измеренную ширину; Рисование строк UIKit использует дробную ширину, что, вероятно, не то, что вам нужно. В качестве альтернативы используйте UIKit для рисования растрового изображения, а затем переместите его в OpenGL (но обратите внимание, что в некоторых случаях glDrawPixels () может работать медленно). Также обратите внимание, что отрисовка строки UIKit (и, вероятно, также -sizeWithFont:...) не потокобезопасна в iOS 5.x: stackoverflow.com/questions/11589768/   -  person tc.    schedule 27.11.2012


Ответы (2)


На самом деле нет необходимости изобретать это колесо, поскольку это именно то, что текстовый движок делает за вас каждый раз, когда вы переносите текст. А что такое текстовый движок? Это основной текст. Если вы спуститесь до уровня Core Text и CTFramesetter раскладывает текст для вас, вы можете узнать, где он помещает разрывы строк, запросив результирующие CTLines.

Документация поможет вам начать:

http://developer.apple.com/library/ios/#documentation/StringsTextFonts/Conceptual/CoreText_Programming/Operations/Operations.html

И есть много хороших руководств в Интернете.

Простой пример:

NSString* s = @"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
@"eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut "
@"enim ad minim veniam, quis nostrud exercitation ullamco laboris "
@"nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor "
@"in reprehenderit in voluptate velit esse cillum dolore eu fugiat "
@"nulla pariatur. Excepteur sint occaecat cupidatat non proident, "
@"sunt in culpa qui officia deserunt mollit.";
NSAttributedString* text = [[NSAttributedString alloc] initWithString:s];

CTFramesetterRef fs =
CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)text);
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, CGRectMake(0,0,200,100000));
CTFrameRef f = CTFramesetterCreateFrame(fs, CFRangeMake(0, 0), path, NULL);
CTFrameDraw(f, NULL);

NSArray* lines = (__bridge NSArray*)CTFrameGetLines(f);
for (id aLine in lines) {
    CTLineRef theLine = (__bridge CTLineRef)aLine;
    CFRange range = CTLineGetStringRange(theLine);
    NSLog(@"%ld %ld", range.location, range.length);
}
CGPathRelease(path);
CFRelease(f);
CFRelease(fs);

Как вы увидите, выходные данные показывают диапазон каждой строки обернутого текста. Разве это не то, что вам нужно?

person matt    schedule 27.11.2012
comment
Простите меня, если это глупый вопрос, поскольку у меня не было возможности разобраться в нем, но теперь, когда в iOS 7 есть NSLayoutManager и NSTextStorage, можем ли мы сделать то же самое, что вы сделали выше, не имея непосредственного отношения к структуре CoreText? Например. используя вместо этого некоторые из этих фреймворков более высокого уровня? - person Senseful; 26.10.2013
comment
Это совсем не глупый вопрос, и да, мы можем! И это намного проще. Менеджер по расположению с радостью сообщит вам полные глифы / текст каждого фрагмента строки. - person matt; 26.10.2013
comment
Отлично, спасибо за информацию. Я разместил это как отдельный вопрос: Как получить информацию о переносе слов с помощью новых API iOS 7? - person Senseful; 26.10.2013

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

+ (NSString*)wrappedString:(NSString*)string withFont:(UIFont*)font andWidth:(float)width {
    NSMutableString *resultString = [[NSMutableString alloc] initWithString:@""];

    CGSize textSize = [string sizeWithFont:font];
    float textWidth = textSize.width;
    if (textWidth < width) {
        return string;
    }
    float wordLength;
    float lineLength;
    NSUInteger length = [string length];
    unichar buffer[length];
    [string getCharacters:buffer range:NSMakeRange(0, length)];

    NSString *singleLine = @"";
    NSString *word = @"";
    NSString *longWord = @"";

    for (NSUInteger i = 0; i < length; i++) {

        unichar character = buffer[i];
        if (character != '\n') {
            word = [NSString stringWithFormat:@"%@%c", word, character];
        }

        if (character == '\n') {
            float wordLength = [word sizeWithFont:font].width;
            float lineLength = [singleLine sizeWithFont:font].width;
            if ((lineLength + wordLength) > width) {
                [resultString appendString:singleLine];
                [resultString appendString:@"\n"];
                singleLine = @"";
                singleLine = [singleLine stringByAppendingFormat:@"%@\n",word];
                word = @"";
            } else {
                singleLine = [singleLine stringByAppendingString: word];
                word = @"";
                [resultString appendString:singleLine];
                [resultString appendString:@"\n"];
                singleLine = @"";
            }
        }

        else if (character == ' ') {
            float wordLength = [word sizeWithFont:font].width;
            float lineLength = [singleLine sizeWithFont:font].width;

            if ((lineLength + wordLength) > width) {
                if (wordLength > textWidth) {
                    [resultString appendString:singleLine];
                    [resultString appendString:@"\n"];
                    singleLine = @"";
                    int j = 0;
                    for (; j < [word length]; j++) {
                        unichar longChar = [word characterAtIndex:j];
                        longWord = [NSString stringWithFormat:@"%@%c", longWord, longChar];
                        float longwordLength = [longWord sizeWithFont:font].width;
                        float longlineLength = [singleLine sizeWithFont:font].width;
                        if ((longlineLength + longwordLength) >= width) {
                            singleLine = [singleLine stringByAppendingString:longWord];
                            word = @"";
                            longWord = @"";
                            break;
                        }
                    }

                }
                [resultString appendString:singleLine];
                [resultString appendString:@"\n"];
                singleLine = @"";
            }
            singleLine = [singleLine stringByAppendingString: word];
            word = @"";
        }
    }

    wordLength = [word sizeWithFont:font].width;
    lineLength = [singleLine sizeWithFont:font].width;

    if (wordLength > 0) {
        if ((lineLength + wordLength) > width) {
            [resultString appendString:singleLine];
            [resultString appendString:@"\n"];
            singleLine = @"";
        }
        singleLine = [singleLine stringByAppendingString:word];
    }


    if (lineLength > 0) {
        [resultString appendString:singleLine];
        [resultString appendString:@"\n"];
    }
    return resultString;
}
person Nils Munch    schedule 27.11.2012
comment
[NSString stringWithFormat:@""]; лол - person trojanfoe; 27.11.2012
comment
Все еще полирую это от оригинала, трояна ... - person Nils Munch; 27.11.2012