iphonesizewithfont

CTFrameGetVisibleStringRange equivalent for iOS programming?


I need a method like CTFrameGetVisibleStringRange that can give me the text that will be rendered in a given size supplied with a line break mode (i.e. word wrap). For example I have a long line of text.. and I have a given rectangle to draw the text wrapped in it, but wherever the text gets trunecated, I continue rendering it in another area where it left off. So I need a method like:

NSString * text = "The lazy fox jumped over the creek";
[text drawAtPoint:CGPointMake(0, 0) forWidth:20 withFont:[UIFont fontWithName:@"Arial" size:10] lineBreakMode:UILineBreakModeWordWrap];
// now I do I know how much it drew before it stopped rendering?

Anyone have any ideas?

**EDITED: Please see my solution.


Solution

  • I had a similar problem, and I used the solution Mike posted.

    It turned out, however, that trimToWord was often giving me a few too many words than could fit on my UILabel size specified. I found out that if I changed the while loop operator to a >= and not just a >, it worked perfectly.

    I also added a few ivars(chopIndex and remainingBody) that I used to get the remaining string so I could display it in my next UILabel.

    Here's the solution I used.

    -(NSString*) rewindOneWord:(NSString*) str{
        // rewind by one word
        NSRange lastspace = [str rangeOfString:@" " options:NSBackwardsSearch];
        if (lastspace.location != NSNotFound){
            int amount = [str length]-lastspace.location;
            chopIndex -= amount;
            return [str substringToIndex:lastspace.location];
        }else {
            // no spaces, lets just rewind 2 characters at a time
            chopIndex -= 2;
            return [str substringToIndex:[str length]-2];
        }
    }
    
    // returns only how much text it could render with the given stipulations   
    -(NSString*) trimToWord:(NSString*)str sizeConstraints:(CGSize)availableSize withFont:(UIFont*)font{
        if(str == @"")
            return str;
    
        CGSize measured = [str sizeWithFont:font constrainedToSize:CGSizeMake(availableSize.width, CGFLOAT_MAX) lineBreakMode:UILineBreakModeWordWrap];
        // 'guess' how much we will need to cut to save on processing time
        float choppedPercent = (((double)availableSize.height)/((double)measured.height));
        if(choppedPercent >= 1.0){
            //entire string can fit in availableSize
            remainingBody = @"";
            return str;
        }
    
        chopIndex = choppedPercent*((double)[str length]);
        str = [str substringToIndex:chopIndex];
        // rewind to the beginning of the word in case we are in the middle of one
        do{
            str = [self rewindOneWord:str];
            measured = [str sizeWithFont:font constrainedToSize:availableSize lineBreakMode:UILineBreakModeWordWrap];
        }while(measured.height>=availableSize.height);
    
        //increment past the last space in the chopIndex
        chopIndex++;
    
        //update the remaining string
        remainingBody = [remainingBody substringFromIndex:chopIndex];
    
        return str;
    }