2009-05-12 3 views
6

Necesito mover el texto que el usuario ha ingresado en una gran línea múltiple UITextView en una más pequeña (pero aún multilínea) UITextView *. Si el usuario ha ingresado más texto de lo que se mostrará en la vista más pequeña, quiero truncar el texto para que se ajuste a todo el texto (truncado) visible. (Ni el gran UITextView ni el más pequeño deben desplazarse.)Longitud de cadena con una fuente determinada para adaptarse a UITextView

¿Cuál es la mejor manera de hacerlo?

Puedo usar un bucle, acortando la cuerda por un carácter cada vez, y luego uso NSString 's sizeWithFont: constrainedToSize: lineBreakMode: para averiguar la altura que necesitaría esta cuerda más corta, y luego comparar eso con la altura que tengo disponible en mi pequeño UITextView, terminando el ciclo cuando la cadena encajará, pero parece lento e incómodo. Debe haber una mejor manera.

Me gustaría decirle al destino UITextView para truncar su miembro de displayText como lo muestra en la pantalla, pero no he podido encontrar la manera de hacerlo.

* Más en este contexto, a partir de un comentario que hizo a continuación:

Tengo una aplicación paisaje. Cambio el diseño de la vista dependiendo de la foto que el usuario elija. Si se trata de una foto de paisaje, la leyenda es más pequeña, solo una línea en la parte inferior de la foto. Si ella elige una foto de retrato, entonces hay un montón de espacio que puedo usar para la leyenda al costado de la foto, así que la leyenda es más grande.

Si el usuario cambia la orientación de su foto de vertical a horizontal, entonces quiero truncar el texto y luego permitirle que lo edite para que tenga sentido. Podría borrarlo, pero preferiría preservarlo para minimizar su tipeo.

Respuesta

3

Escribí el siguiente método recursivo y la API pública para hacer esto correctamente. El feo factor de chocolate es el tema de this question.

#define kFudgeFactor 15.0 
#define kMaxFieldHeight 9999.0 

// recursive method called by the main API 
-(NSString*) sizeStringToFit:(NSString*)aString min:(int)aMin max:(int)aMax 
{ 
if ((aMax-aMin) <= 1) 
    { 
    NSString* subString = [aString substringToIndex:aMin]; 
    return subString; 
    } 

int mean = (aMin + aMax)/2; 
NSString* subString = [aString substringToIndex:mean]; 

CGSize tallerSize = CGSizeMake(self.frame.size.width-kFudgeFactor,kMaxFieldHeight); 
CGSize stringSize = [subString sizeWithFont:self.font constrainedToSize:tallerSize lineBreakMode:UILineBreakModeWordWrap]; 

if (stringSize.height <= self.frame.size.height) 
    return [self sizeStringToFit:aString min:mean max:aMax]; // too small 
else  
     return [self sizeStringToFit:aString min:aMin max:mean];// too big 
} 

-(NSString*)sizeStringToFit:(NSString*)aString 
{ 

CGSize tallerSize = CGSizeMake(self.frame.size.width-kFudgeFactor,kMaxFieldHeight); 
CGSize stringSize = [aString sizeWithFont:self.font constrainedToSize:tallerSize lineBreakMode:UILineBreakModeWordWrap]; 

// if it fits, just return 
if (stringSize.height < self.frame.size.height) 
    return aString; 

// too big - call the recursive method to size it  
NSString* smallerString = [self sizeStringToFit:aString min:0 max:[aString length]]; 
return smallerString; 
} 
+0

este código es fantástico, gracias, pero he encontrado dos situaciones en las que falla: 1) si tiene muchos espacios en la cadena 2) la "<" en la línea "if (stringSize.height SpaceDog

1

Sugeriría adoptar un enfoque ligeramente diferente y ver si puede usar un UILabel en lugar del UITextView más pequeño.

UILabels se puede configurar para que sea multilínea como una UITextView a través de su propiedad numberOfLines.

UILabels también tienen una propiedad lineBreakMode y creo que el valor predeterminado de esa propiedad hará el efecto de truncamiento exacto que está buscando.

+1

Pero no creo que UILabels pueda ser editable, ¿o sí? Quiero que el usuario pueda ingresar texto en estos dos campos. (Especialmente una vez que lo he truncado, el texto probablemente no tenga sentido.) –

+0

Tiene razón UILabels no son editables. Supongo que me has dejado perplejo por ahora. En general, me pregunto cuál sería la experiencia del usuario al editar su UITextView más pequeño. ¿Puede el usuario editar todo el texto o solo la parte que no se ha truncado? Si el usuario puede editar todo el texto, ¿cómo puede el usuario decir que sus modificaciones han tenido efecto si se truncan una vez que finaliza su sesión de edición? Por estas razones, podría considerar mantener la vista más pequeña como un resumen de solo lectura del texto. –

+0

Se necesita un poco más de información. Tengo una aplicación de paisaje. Lo que sucede es que cambio el diseño de la vista según la foto que el usuario elija. Si se trata de una foto de paisaje, la leyenda es más pequeña, solo una línea en la parte inferior de la foto. Si eligen una foto de retratos, entonces hay mucho espacio que puedo usar para la leyenda al costado de la foto, así que la leyenda es más grande. Si el usuario cambia la orientación de su foto de vertical a horizontal, entonces quiero truncar el texto y luego permitirle que lo cambie para que tenga sentido. –

1

Creo que Jonathan estaba en algo acerca de la UILabel ...

Por lo tanto, el usuario termina la edición de la UITextView, se obtiene la cadena de texto y pasarlo a la UILabel. Cambia el alfa de UITextView a 0 y/o lo elimina de la supervista. Posiblemente, almacene el texto completo no truncado en un ivar.

UILabels no son "editables", sin embargo, puede detectar un toque con un UILabel (o es superview). Cuando detecta el toque en UILabel, simplemente restaura el UITextView oculto y restaura la cadena que guardó.

A veces el SDK es un dolor, pero casi siempre gana la pelea. Muchas veces, es mejor ajustar su diseño a las convenciones de UIKit

2

Esto no es realmente una solución, pero proporciona una buena poing inicial para el cálculo.

Si usa sizeWithFont: constrainedToSize: lineBreakMode: de NSString, obtendrá una altura vertical para su texto. Si divides eso por la altura inicial de tu fuente, obtienes la cantidad de líneas en toda la cadena. Dividir [contador NSString] por ese número le da una aproximación al número de caracteres por línea.Esto supone que la cadena es homogénea y será inexacta si alguien escribe (por ejemplo) 'iiiiiiiiiii ...' como opuesto a 'MMMMMMMMM ...'.

También puede dividir el cuadro delimitador entre la altura de la fuente relevante leading para obtener el número de líneas que caben dentro de su cuadro delimitador.

Multiplicando caracteres por línea por el número de líneas que da un punto de partida para la búsqueda de texto que cabe.

se podría calcular el margen de error en esta figura por haciendo el mismo cálculo para esas cadenas 'iiiiii ...' y "MMMMMM ... '"

+0

De acuerdo, este es un buen enfoque, y también lo es hacer un corte binario en la longitud de la cadena utilizando el método anterior. Sigo esperando que haya una mejor manera, pero ahora tengo la sensación de que no. –

+0

La solución es Courier. Fuentes de ancho fijo, sin matemática ... –

+0

@Tristan, solo si ignora los espacios en blanco al envolver. Sin embargo, sería un problema más simple. –

Cuestiones relacionadas