How to determine which part of an nsstring fits in

2019-06-18 09:06发布

问题:

This is about PDF formatting using core graphics. But it could be about any type of paging when it comes to printing a string on two pages.

I need to spread a string over several pages. For smaller strings this is no real problem. For those I use the NSString UIKit extensions sizeWithFont to determine, whether the full text fits on the current page or not. If it does, then I print it with drawInRect and if it does not then I move it to the next page.

Works fine but is not suitable for longer strings. In my app individual strings (provided by the user) may even be longer than a full page.

When there is a full page or some remaining space of a page given, how do I determine exactly what part of the NSString can be displayed in that given rect so that I can cut it off and print the remaining string (or at least a part of it) on the next page?

I thought of an alternative. That is drawing the full text into an off screen graphics context and then cut the created image into slices which fit into the free space on the pages. But if I do that then I need to place the cutting line exactly between the text lines. And frankly I am not sure how to figure out where the text containing graphic can be safely cut into two (or more) parts without cutting a line of text in its middle.

I hope I expressed myself understandable and that somebody comes to the rescue.

回答1:

In Core Text, there is a very relevant function, whose last argument (a pointer to a CFRange) is defined as follows:

fitRange
On return, contains the range of the string that actually fit in the constrained size.


回答2:

For the general case where you are not using Core Text, you can just keep adding characters to the string until its size is too large. This can be quite fast, and if you have really enormous strings (as you seem to), you can speed it up with a simple binary search.

If you're using Core Text, just create a CTFrame for the area you want to fill and then ask it for the range it used with CTFrameGetVisibleStringRange().



回答3:

How about using an approximation?

- (NSInteger)lengthOfString:(NSString *)str
           thatFitsIntoView:(UIView *)displayView
                   withFont:(UIFont *)font
{
    CGSize textSize = [str sizeWithFont:font
                               forWidth:displayView.frame.size.width
                          lineBreakMode:NSLineBreakByWordWrapping];

    NSInteger lengthThatFits;
    if (textSize.height > displayView.frame.size.height) {
        lengthThatFits = str.length * displayView.frame.size.height / textSize.height;
    } else {
        lengthThatFits = str.length;
    }

    return lengthThatFits;
}

Call it like:

NSString *str = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin eros"
    "nulla, commodo eget fringilla nec, varius nec ligula. Donec pulvinar eros sed diam"
    "euismod scelerisque. Mauris imperdiet leo at elit vulputate ullamcorper. Nullam"
    "condimentum odio a lacus tempus ac pretium justo tincidunt.";

UIFont *font = [UIFont systemFontOfSize:16.0]; // or whatever
UIView *displayView = // the view in which you intend to present the string

NSInteger length = [self lengthOfString:str thatFitsIntoView:displayView usingFont:font];
NSString *displayedString = [str substringToIndex:length];