iphone - Líneas resultantes de UILabel con UILineBreakModeWordWrap
cocoa-touch iphone-sdk-3.0 (5)
No creo que haya ninguna bala de plata para esto.
Aquí hay un método de categoría que parece funcionar para los pocos casos de prueba básicos que le presenté. No hay garantías de que no se rompa con algo complejo!
La forma en que funciona es moverse a través de las pruebas de cadena para ver si un rango de palabras se ajusta al ancho de la etiqueta. Cuando calcula que el rango actual es demasiado amplio, registra el rango de último ajuste como una línea.
No digo que esto sea eficiente. Una mejor manera puede ser implementar tu propia UILabel ...
@interface UILabel (Extensions)
- (NSArray*) lines;
@end
@implementation UILabel (Extensions)
- (NSArray*) lines
{
if ( self.lineBreakMode != UILineBreakModeWordWrap )
{
return nil;
}
NSMutableArray* lines = [NSMutableArray arrayWithCapacity:10];
NSCharacterSet* wordSeparators = [NSCharacterSet whitespaceAndNewlineCharacterSet];
NSString* currentLine = self.text;
int textLength = [self.text length];
NSRange rCurrentLine = NSMakeRange(0, textLength);
NSRange rWhitespace = NSMakeRange(0,0);
NSRange rRemainingText = NSMakeRange(0, textLength);
BOOL done = NO;
while ( !done )
{
// determine the next whitespace word separator position
rWhitespace.location = rWhitespace.location + rWhitespace.length;
rWhitespace.length = textLength - rWhitespace.location;
rWhitespace = [self.text rangeOfCharacterFromSet: wordSeparators options: NSCaseInsensitiveSearch range: rWhitespace];
if ( rWhitespace.location == NSNotFound )
{
rWhitespace.location = textLength;
done = YES;
}
NSRange rTest = NSMakeRange(rRemainingText.location, rWhitespace.location-rRemainingText.location);
NSString* textTest = [self.text substringWithRange: rTest];
CGSize sizeTest = [textTest sizeWithFont: self.font forWidth: 1024.0 lineBreakMode: UILineBreakModeWordWrap];
if ( sizeTest.width > self.bounds.size.width )
{
[lines addObject: [currentLine stringByTrimmingCharactersInSet:wordSeparators]];
rRemainingText.location = rCurrentLine.location + rCurrentLine.length;
rRemainingText.length = textLength-rRemainingText.location;
continue;
}
rCurrentLine = rTest;
currentLine = textTest;
}
[lines addObject: [currentLine stringByTrimmingCharactersInSet:wordSeparators]];
return lines;
}
@end
utilizar así:
NSArray* lines = [_theLabel lines];
int count = [lines count];
Tengo una UILabel cuyo tamaño se calcula con el método sizeWithFont:
El modo de salto de línea se establece en UILineBreakModeWordWrap
(el mismo indicador se usa al calcular el tamaño con sizeWithFont:
...
Todo funciona bien, la etiqueta tiene el tamaño adecuado y muestra mi texto según sea necesario.
Ahora necesito saber las líneas que se usan para mostrar la etiqueta (o las líneas que se generan cuando se usa sizeWithFont:
. Técnicamente podría escribir mi propia implementación de ruptura de línea en función de los espacios / devoluciones de entrada, pero luego no se garantizará de la misma manera que la implementación de Apple y, por lo tanto, las líneas resultantes no serán las que se utilicen para calcular el tamaño del texto. , no importa el hecho de reinventar la rueda.
Lo ideal sería pasar mi cadena, especificar el ancho y el modo de salto de línea y recibir una serie de cadenas que representan las líneas visuales del texto.
¿Alguna idea de cómo hacer que esto suceda de la manera más elegante?
Para calcular la cantidad de líneas que tiene una UILabel
después de envolver su texto, deberá encontrar la fuente (altura de línea) de la UILabel
de su UILabel
( label.font.leading
) y luego dividir la altura de su UILabel
por La altura de cada línea para obtener el número de líneas.
Aquí hay un ejemplo:
- (void)viewDidLoad {
[super viewDidLoad];
UILabel *label = [[[UILabel alloc] initWithFrame:CGRectZero] autorelease];
label.numberOfLines = 0;
label.lineBreakMode = UILineBreakModeWordWrap;
label.text = @"Some really really long string that will cause the label''s text to wrap and wrap and wrap around. Some really really long string that will cause the label''s text to wrap and wrap and wrap around.";
CGRect frame = label.frame;
frame.size.width = 150.0f;
frame.size = [label sizeThatFits:frame.size];
label.frame = frame;
CGFloat lineHeight = label.font.leading;
NSUInteger linesInLabel = floor(frame.size.height/lineHeight);
NSLog(@"Number of lines in label: %i", linesInLabel);
[self.view addSubview:label];
}
O, puedes hacerlo en dos líneas:
[label sizeToFit];
int numLines = (int)(label.frame.size.height/label.font.leading);
Simplemente llame al método de abajo y pase UILabel
o UITextView
:
-(NSInteger)getNumberOfLinesInLabelOrTextView:(id)obj
{
NSInteger lineCount = 0;
if([obj isKindOfClass:[UILabel class]])
{
UILabel *label = (UILabel *)obj;
// This method is deprecated in iOS 7.0 or later
// CGSize requiredSize = [label.text sizeWithFont:label.font constrainedToSize:label.frame.size lineBreakMode:label.lineBreakMode];
CGSize requiredSize = [label.text boundingRectWithSize:CGSizeMake(CGRectGetWidth(label.frame), CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:label.font} context:nil].size;
int charSize = label.font.leading;
int rHeight = requiredSize.height;
lineCount = rHeight/charSize;
}
else if ([obj isKindOfClass:[UITextView class]])
{
UITextView *textView = (UITextView *)obj;
lineCount = textView.contentSize.height / textView.font.leading;
}
return lineCount;
}
Ahora llama a este método:
NSLog(@"%d",[self getNumberOfLinesInLabelOrTextView:label]);
NSLog(@"%d",[self getNumberOfLinesInLabelOrTextView:textView]);
ACTUALIZADO: SWIFT CODE
func getNumberOfLinesInLabelOrTextView(obj:AnyObject) -> NSInteger {
var lineCount: NSInteger = 0
if (obj.isKindOfClass(UILabel)) {
let label: UILabel = obj as! UILabel
let requiredSize: CGSize = (label.text)!.boundingRectWithSize(CGSizeMake(CGRectGetWidth(label.frame), CGFloat.max), options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: [NSFontAttributeName: label.font], context: nil).size
let charSize: CGFloat = label.font.leading
let rHeight: CGFloat = requiredSize.height
lineCount = (NSInteger)(rHeight/charSize)
}
else if (obj.isKindOfClass(UITextView)){
let textView: UITextView = obj as! UITextView
lineCount = (NSInteger)(textView.contentSize.height / textView.font.leading)
}
return lineCount
}
Ahora llama a este método:
println("%d /(self.getNumberOfLinesInLabelOrTextView(textView))")
println("%d /(self.getNumberOfLinesInLabelOrTextView(label))")
Nota: leading
- usa lineHeight. no devuelve líder real. Será desaprobado formalmente en el futuro.
para Xcode 7 y versiones superiores, la respuesta de TheTiger necesita una actualización comentada en el siguiente código:
-(NSInteger)getNumberOfLinesInLabelOrTextView:(id)obj
{
NSInteger lineCount = 0;
if([obj isKindOfClass:[UILabel class]])
{
UILabel *label = (UILabel *)obj;
CGSize requiredSize = [label.text boundingRectWithSize:CGSizeMake(CGRectGetWidth(label.frame), CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:label.font} context:nil].size;
int charSize = label.font.leading;
// now listen , you need to set the text or label with only 1
// then nslog(@"%d",charSize);
// then change the line int charSize = label.font.leading; into
// int charSize = the printed value in case of 1 line
int rHeight = requiredSize.height;
lineCount = rHeight/charSize;
}
else if ([obj isKindOfClass:[UITextView class]])
{
UITextView *textView = (UITextView *)obj;
lineCount = textView.contentSize.height / textView.font.leading;
}
return lineCount;
}
esto solo funcionará si está utilizando la misma fuente y tamaño, no es un movimiento inteligente, pero me ayudó y quería compartirlo como la solución actual que conozco
Actualizado para Swift 3
Para calcular el número de líneas que tiene UILabel, después de ajustar su texto, necesita dividir la altura de su línea múltiple UILabel por la altura de cada línea ( ascender
).
Al intentar la respuesta de Adolfo, por alguna razón, label.font.leading
devolvió 0.0, así que usé label.font.ascender
, que devuelve la altura desde la línea de base hasta la parte superior del marco de UILabel
. Vea la imagen de abajo.
//Makes label go to another line if necessary
label.numberOfLines = 0 //Set num of lines to infinity
label.lineBreakMode = .byWordWrapping
label.sizeToFit()
let numLines = Int(label.frame.size.height/label.font.ascender)