Эквивалент CTFrameGetVisibleStringRange для программирования iOS?

Мне нужен такой метод, как CTFrameGetVisibleStringRange, который может дать мне текст, который будет отображаться в заданном размере, снабженном режимом разрыва строки (т. е. переносом слов). Например, у меня есть длинная строка текста... и у меня есть заданный прямоугольник для рисования текста, обернутого в него, но везде, где текст усекается, я продолжаю отображать его в другой области, где он остановился. Поэтому мне нужен такой метод, как:

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?

У кого-нибудь есть идеи?

** EDITED: Пожалуйста, посмотрите мое решение.


person Mike S    schedule 20.03.2011    source источник


Ответы (3)


У меня была похожая проблема, и я использовал решение, опубликованное Майком.

Однако оказалось, что trimToWord часто давал мне слишком много слов, чем мог уместиться в указанном размере UILabel. Я обнаружил, что если я изменю оператор цикла while на >=, а не просто на >, все будет работать отлично.

Я также добавил несколько иваров (chopIndex и remainingBody), которые я использовал для получения оставшейся строки, чтобы я мог отобразить ее в своем следующем UILabel.

Вот решение, которое я использовал.

-(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;
}
person teradyl    schedule 04.04.2011
comment
Я наградил вас ответом на этот вопрос, поскольку вы улучшили мое решение. Спасибо @teradyl - person Mike S; 15.05.2013

Вот решение. Это довольно быстро. Он «угадывает», где сначала нарезать, а затем слово за словом откатывается назад. Вызовы sizewithFont довольно дороги, поэтому этот начальный шаг «угадывания» важен. Основной метод — trimToWord: sizeConstraints:withFont.

Не стесняйтесь комментировать, как я мог бы улучшить это.

-(NSString*) rewindOneWord:(NSString*) str{
    // rewind by one word
    NSRange lastspace = [str rangeOfString:@" " options:NSBackwardsSearch];
    if (lastspace.location != NSNotFound){
        int amount = [str length]-lastspace.location;
        return [str substringToIndex:lastspace.location];
    }else {
        // no spaces, lets just rewind 2 characters at a time
        return [str substringToIndex:[str length]-2];
    }
}

// returns only how much text it could render with the given stipulations   
-(NSString*) trimToWord:(NSString*) str sizeConstraints:(CGSize) avail withFont:(UIFont*) font{
    CGSize measured = [str sizeWithFont:font constrainedToSize:CGSizeMake(avail.width, 1000000) lineBreakMode:UILineBreakModeWordWrap];
    // 'guess' how much we will need to cut to save on processing time
    float choppedPercent = (((double)avail.height)/((double)measured.height));
    if (choppedPercent >= 1.0){
        return str;
    }

    int 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
    str = [self rewindOneWord:str];
    measured = [str sizeWithFont:font constrainedToSize:avail lineBreakMode:UILineBreakModeWordWrap];
    while (measured.height>avail.height){
        str = [self rewindOneWord:str];
        measured = [str sizeWithFont:font constrainedToSize:avail lineBreakMode:UILineBreakModeWordWrap];
    }
    return str;
}
person Mike S    schedule 27.03.2011

Я не думаю, что есть замена для CTFrameGetVisibleStringRange, хотя мы можем получить то же самое с помощью метода, описанного ниже.

- (CGSize)sizeWithFont:(UIFont *)font forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode

Документация Apple

http://developer.apple.com/library/ios/#documentation/uikit/reference/NSString_UIKit_Additions/Reference/Reference.html

ОТРЕДАКТИРОВАНО: приведенный ниже код показывает мой подход

NSString * text = "The lazy fox jumped over the creek";

NSArray* m_Array = [text  componentsSeparatedByCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:@" "]];

CGSize mySize = CGSizeMake(300,180);
NSMutableString* myString = [[NSMutableString alloc] initWithString:@""];

//The below code till the end of the while statement could be put in separate function.

CGSize tempSize = CGSizeMake(0,0);
NSInteger index = 0 ;
do
{
      [myString  appendString:[m_Array objectAtIndex:index]];
      tempSize  = [myString  sizeWithFont:myfont constrainedToSize: 
      CGSizeMake(mySize.width, CGFLOAT_MAX) lineBreakMode: UILineBreakModeWordWrap];
      index++;

}while(tempSize.height < mySize.height && index <= [m_Array count])

//Remove the string items from m_Array till the (index-1) index,

[self RemoveItems:m_Array tillIndex:(index-1)];//Plz define you own

//you have the myString which could be fitted in CGSizeMake(300,180);


//Now start with remaining Array items with the same way as we have done above.
}
person Jhaliya - Praveen Sharma    schedule 20.03.2011
comment
Это дает мне размер. Мне нужно знать, какая часть строки была нарисована. - person Mike S; 20.03.2011
comment
@Mike Simmons: Правильно, с помощью этой функции вы можете проверить минимальную группу слов для заполнения данного прямоугольника. - person Jhaliya - Praveen Sharma; 20.03.2011
comment
Мне нравится, куда вы направляетесь с этим решением, но myString заканчивается без пробелов. Кроме того, даже если вы добавите обратно пробелы, это не позволит ситуации, когда между словами есть 2 пробела. Мне нужно, чтобы моя строка была идеальным представлением того, что было раньше. - person Mike S; 20.03.2011
comment
Кроме того, очень медленно вызывать sizeWitFont снова и снова для каждого слова, поэтому я надеялся, что в платформах iOS есть какой-то эквивалент CTFrameGetVisibleStringRange. В нынешнем виде единственное решение, которое я вижу для вашего кода, - это проходить символ за символом, что, вероятно, снова будет в 5 раз медленнее. - person Mike S; 20.03.2011
comment
я забыл добавить пробел после [split objectAtIndex:index]]; - person Jhaliya - Praveen Sharma; 20.03.2011
comment
Пожалуйста, дайте мне знать о результате, может быть, нам нужно внести больше изменений, чтобы заставить его работать, - person Jhaliya - Praveen Sharma; 20.03.2011