Truncar UILabel en una location específica

Utilizo una vista de tabla para mostrar una list de libros, donde cada celda tiene una UILabel que muestra el nombre del libro y otra UILabel muestra el (los) autor (es) del libro.

Mi pregunta es sobre la label del autor (es). Un libro puede tener múltiples autores, y quiero que se comporte de la siguiente manera:

  • Si el libro tiene una label de autor ('John Colman') debe ser: "John Colman"
  • Si el libro tiene más de un autor ('John Colman', 'Bob Night', 'Michael'), la label debe ser: "John Colman +2 autores"

Ahora el problema es este, quiero que la label se trunque antes del '+'. Por ejemplo, si el nombre del primer autor es largo, digamos 'Benjamin Walter Jackson', quiero que la label se vea así:

 "Benjamin Walter Ja... +2 authors" 

El comportamiento pnetworkingeterminado, por supuesto, trunca la label al final, por lo que se ve así:

 "Benjamin Walter Jackson +2 au..." 

Si utilizo el truncado medio, no hay promise de que truncará la label en el lugar correcto (antes del '+').

Estoy buscando una manera de hacerlo y tan eficiente como sea posible, sin afectar el performance de desplazamiento de la vista de tabla.

Editar: generalizó la solución para que funcione con cualquier cadena de "location de truncamiento". La versión anterior solo se trunca en la instancia de cadena @" +" . Editar le permite definir dónde desea que suceda el truncamiento.


Tomé mi respuesta de esta pregunta (que fue una respuesta modificada de la respuesta en este sitio ) y la adapté para satisfacer sus necesidades. Cree una nueva interfaz NSString donde pueda enviar su cadena para que se NSString de forma personalizada.

NOTA: esta solución es solo para iOS 7+. Para usar en iOS 6, use sizeWithFont: lugar de sizeWithAttributes: en el file NSString + TruncateToWidth.m .

NSString + TruncateToWidth.h

 @interface NSString (TruncateToWidth) - (NSString*)stringByTruncatingAtString:(NSString *)string toWidth:(CGFloat)width withFont:(UIFont *)font; @end 

NSString + TruncateToWidth.m

 #import "NSString+TruncateToWidth.h" #define ellipsis @"…" @implementation NSString (TruncateToWidth) - (NSString*)stringByTruncatingAtString:(NSString *)string toWidth:(CGFloat)width withFont:(UIFont *)font { // If the string is already short enough, or // if the 'truncation location' string doesn't exist // go ahead and pass the string back unmodified. if ([self sizeWithAttributes:@{NSFontAttributeName:font}].width < width || [self rangeOfString:string].location == NSNotFound) return self; // Create copy that will be the returned result NSMutableString *truncatedString = [self mutableCopy]; // Accommodate for ellipsis we'll tack on the beginning width -= [ellipsis sizeWithAttributes:@{NSFontAttributeName:font}].width; // Get range of the passed string. Note that this only works to the first instance found, // so if there are multiple, you need to modify your solution NSRange range = [truncatedString rangeOfString:string]; range.length = 1; while([truncatedString sizeWithAttributes:@{NSFontAttributeName:font}].width > width && range.location > 0) { range.location -= 1; [truncatedString deleteCharactersInRange:range]; } // Append ellipsis range.length = 0; [truncatedString replaceCharactersInRange:range withString:ellipsis]; return truncatedString; } @end 

Usarlo:

 // Make sure to import the header file where you want to use it myLabel.text = [@"Benjamin Walker Jackson + 2 authors" stringByTruncatingAtString:@" +" toWidth:myLabel.frame.size.width withFont:myLabel.font]; // Sample Result: Benjamin Walte... + 2 authors 

UILabel no puede manejar esa function truncada que no sea iOS 7. o debería truncar la cadena de acuerdo con la longitud fija (calculada con tamaño de fuente uilabel), pero eso es un dolor de cabeza, o puede usar dos UILabels. 1 uilabel con tamaño fijo para el primer autor. se truncará automáticamente al final como ya sabe. entonces la segunda uilabel debe dibujarse exactamente en el lado derecho de la primera label con "+ 2 otros autores".

Editar: echar un vistazo a @ Stonz2 respuesta

Esta respuesta está basada en https://stackoverflow.com/a/30813691/2123122 . Aquí está el código de ejemplo.

 @interface CustomLabel() @property (nonatomic, retain) NSLayoutManager *layoutManager; @property (nonatomic, retain) NSTextContainer *textContainer; @property (nonatomic, retain) NSTextStorage *textStorage; @end @implementation CustomLabel - (instancetype)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) { [self configureTextkitStack]; } return self; } - (instancetype)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { [self configureTextkitStack]; } return self; } - (void)configureTextkitStack { _textContainer = [[NSTextContainer alloc] init]; _textContainer.lineFragmentPadding = 0; _textContainer.maximumNumberOfLines = self.numberOfLines; _textContainer.lineBreakMode = self.lineBreakMode; _textContainer.widthTracksTextView = YES; _layoutManager = [[NSLayoutManager alloc] init]; [_layoutManager addTextContainer:self.textContainer]; [_textContainer setLayoutManager:self.layoutManager]; _textStorage = [[NSTextStorage alloc] init]; [_textStorage addLayoutManager:self.layoutManager]; [self.layoutManager setTextStorage:_textStorage]; } - (NSRange)rangeForTokenInsertion { self.textContainer.size = self.bounds.size; if (self.attributedText.length > 0 ){ [self.textStorage setAttributedString:[[NSMutableAttributedString alloc]initWithAttributedString:self.attributedText]]; } if (self.text.length == 0) { return NSMakeRange(NSNotFound, 0); } NSInteger glyphIndex = [self.layoutManager glyphIndexForCharacterAtIndex:self.textStorage.length - 1]; NSRange range = [self.layoutManager truncatedGlyphRangeInLineFragmentForGlyphAtIndex:glyphIndex]; return range; } 

Ahora puedes usar esto de la siguiente manera:

  NSRange range = [self.label rangeForTokenInsertion]; NSString *token = @"...+2 authors"; if (range.location != NSNotFound ) { range.length += token.length; range.location -= token.length; } if (range.location != NSNotFound) { NSString *finalString = [self.label.text stringByReplacingCharactersInRange:range withString:token]; self.label.text = finalString; } 

He creado una subclass UILabel llamada ResponsiveLabel que maneja token de truncamiento personalizado, así como la aplicación de styles a patrones como userhandles, URL, hashtags, etc.