Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return a word-wrapped NSString from a longer NSString [duplicate]

Possible Duplicate:
UITextView : get text with wrap info

I have been scouting the NSString library and numerous libraries for a function that can take a long string like this :

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.

and together with a CGSize or float indicating the width, and the font being used, and return me a string with \n breaks and the words wrapped.

Result (roughly) :

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. 

I already know that UITextViews and such do this, but this is not helpful since I need to render the text in a raw OpenGL landscape, so I am using no regular UI elements.

I know this either exists as a framework, or a public class somewhere. I just simply can't find any unified way of handling this.

I imagine it is close to [NSString sizeWithFont:forWidth:lineBreakMode:], but i do not need the size, I need the string itself.

like image 247
Nils Munch Avatar asked Nov 27 '12 13:11

Nils Munch


2 Answers

There really is no need to reinvent this wheel, since it is exactly what the text engine does for you every time you wrap text. And what is the text engine? It is Core Text. If you drop down to the level of Core Text and have a CTFramesetter lay out the text for you, you can learn where it is putting the line breaks by asking for the resulting CTLines.

The documentation will get you started:

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

And there are lots of good tutorials on the Web.

Simple example:

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);

As you will see, the output shows the range of each line of wrapped text. Isn't this the sort of thing you're after?

like image 101
matt Avatar answered Sep 30 '22 01:09

matt


Seems like there is no factory way of doing this, so have started constructing a class function to handle this, based on the solution introduced at this closely related stack :

+ (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;
}
like image 31
Nils Munch Avatar answered Sep 30 '22 00:09

Nils Munch