¿CTFrameGetVisibleStringRange equivalente para la programación de iOS?
-
27-10-2019 - |
Pregunta
Necesito un método como CTFrameGetVisiblEdrinRringRange que pueda darme el texto que se representará en un tamaño dado suministrado con un modo de ruptura de línea (es decir, envoltura de palabras). Por ejemplo, tengo una larga línea de texto ... y tengo un rectángulo dado para dibujar el texto envuelto en él, pero donde sea que el texto se truneble, sigo representando en otra área donde lo dejó. Entonces necesito un método como:
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?
¿Alguien tiene alguna idea?
** Editado: por favor vea mi solución.
Solución
Tuve un problema similar y utilicé la solución que Mike publicó.
Resultó, sin embargo, que trimToWord
A menudo me estaba dando demasiadas palabras de las que podían encajar en mi tamaño de uilabel especificado. Descubrí que si cambiaba el operador de bucle While a A> = y no solo a>, funcionó perfectamente.
También agregué algunos Ivars (chopIndex
y remainingBody
) que solía obtener la cadena restante para poder mostrarla en mi próximo uilabel.
Aquí está la solución que utilicé.
-(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;
}
Otros consejos
Aquí hay una solución. Es bastante rápido. 'Adivina' dónde cortar primero y luego retrocede la palabra por palabra. Las llamadas sizewithfont son bastante costosas, por lo que este paso intial de "adivinar" es importante. El método principal es Trimtoword: SizeConstraints: Withfont.
Siéntase libre de comentar cómo podría mejorar esto.
-(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;
}
No creo que hay subsituto para CTFrameGetVisibleStringRange
, Aunque podemos obtener lo mismo con el uso del siguiente método.
- (CGSize)sizeWithFont:(UIFont *)font forWidth:(CGFloat)width lineBreakMode:(UILineBreakMode)lineBreakMode
Documentación de Apple
Editado: el siguiente código muestra mi enfoque
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.
}