CGRect para el ajuste seleccionado UITextRange para text multilínea?

He usado esta respuesta para crear un CGRect para un cierto range de text.

En este UITextView he configurado su text attributedText (así que tengo un montón de text de estilo con diferentes tamaños de glifos).

Esto funciona muy bien para la primera línea de text que está alineado a la izquierda, pero tiene algunos resultados realmente extraños cuando se trabaja con NSTextAlignmentJustified o NSTextAlignmentCenter .

Tampoco se calcula correctamente cuando las líneas se envuelven o (a veces) si hay \n saltos de línea.

Obtengo cosas como esta (esta está alineada en el centro):

introduzca la descripción de la imagen aquí

Cuando en su lugar espero esto:

introduzca la descripción de la imagen aquí

Éste tiene un salto de línea \n , los primeros dos bits de código se resaltaron con éxito, pero el último more code for you to see no fue porque el ajuste de text no se tiene en count en los cálculos x, y.

Aquí está mi implementación:

 - (void)formatMarkdownCodeBlockWithAttributes:(NSDictionary *)attributesDict withHighlightProperties:(NSDictionary *)highlightProperties forFontSize:(CGFloat)pointSize { NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"`.+?`" options:NO error:nil]; NSArray *matchesArray = [regex matchesInString:[self.attributedString string] options:NO range:NSMakeRange(0, self.attributedString.length)]; for (NSTextCheckingResult *match in matchesArray) { NSRange range = [match range]; if (range.location != NSNotFound) { self.textView.attributedText = self.attributedString; CGRect codeRect = [self frameOfTextRange:range forString:[[self.attributedString string] substringWithRange:range] forFontSize:pointSize]; UIView *highlightView = [[UIView alloc] initWithFrame:codeRect]; highlightView.layer.cornerRadius = 4; highlightView.layer.borderWidth = 1; highlightView.backgroundColor = [highlightProperties valueForKey:@"backgroundColor"]; highlightView.layer.borderColor = [[highlightProperties valueForKey:@"borderColor"] CGColor]; [self.contentView insertSubview:highlightView atIndex:0]; [self.attributedString addAttributes:attributesDict range:range]; //strip first and last ` [[self.attributedString mutableString] replaceOccurrencesOfString:@"(^`|`$)" withString:@" " options:NSRegularExpressionSearch range:range]; } } } - (CGRect)frameOfTextRange:(NSRange)range forString:(NSString *)string forFontSize:(CGFloat)pointSize { self.textView.selectedRange = range; UITextRange *textRange = [self.textView selectedTextRange]; CGRect rect = [self.textView firstRectForRange:textRange]; //These three lines are a workaround for getting the correct width of the string since I'm always using the monospaced Menlo font. rect.size.width = ((pointSize / 1.65) * string.length) - 4; rect.origin.x+=2; rect.origin.y+=2; return rect; } 

Ah, y en caso de que quieras, aquí está la secuencia con la que juego:

 *This* is **awesome** @mention `code` more \n `code and code` #hashtag [markdown](http://google.com) __and__ @mention2 {#FFFFFF|colonetworking text} This**will also** work but ** will not ** **work** Also, some `more code for you to see` 

Nota: no sugiera que utilice TTTAttributedLabel o OHAttributedLabel .

Creo que todos sus problemas se deben a un order incorrecto de instrucciones.

Tienes que

  1. Establecer aligment de text
  2. Encuentra las subcadenas necesarias y agrega attributes específicos a ellas.
  3. Y solo entonces resaltar cadenas con sub-vistas.

Además, no necesitará usar "una solución para get el ancho correcto de la cadena ya que siempre estoy usando la fuente monoespaciada de Menlo" en tal caso.

He simplificado un poco el código para hacerlo más comprensible.

Resultado: introduzca la descripción de la imagen aquí

 - (void)viewDidLoad { [super viewDidLoad]; NSDictionary *basicAttributes = @{ NSFontAttributeName : [UIFont boldSystemFontOfSize:18], NSForegroundColorAttributeName : [UIColor blackColor] }; NSDictionary *attributes = @{ NSFontAttributeName : [UIFont systemFontOfSize:15], NSForegroundColorAttributeName : [UIColor darkGrayColor]}; _textView.attributedText = [[NSAttributedString alloc] initWithString: @"*This* is **awesome** @mention `code` more \n `code and code` #hashtag [markdown](http://google.com) __and__ @mention2 {#FFFFFF|colonetworking text} This**will also** work but ** will not ** **work** Also, some `more code for you to see`" attributes:attributes]; _textView.textAlignment = NSTextAlignmentCenter; [self formatMarkdownCodeBlockWithAttributes:basicAttributes]; } - (void)formatMarkdownCodeBlockWithAttributes:(NSDictionary *)attributesDict { NSMutableString *theString = [_textView.attributedText.string mutableCopy]; NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"`.+?`" options:NO error:nil]; NSArray *matchesArray = [regex matchesInString:theString options:NO range:NSMakeRange(0, theString.length)]; NSMutableAttributedString *theAttributedString = [_textView.attributedText mutableCopy]; for (NSTextCheckingResult *match in matchesArray) { NSRange range = [match range]; if (range.location != NSNotFound) { [theAttributedString addAttributes:attributesDict range:range]; } } _textView.attributedText = theAttributedString; for (NSTextCheckingResult *match in matchesArray) { NSRange range = [match range]; if (range.location != NSNotFound) { CGRect codeRect = [self frameOfTextRange:range]; UIView *highlightView = [[UIView alloc] initWithFrame:codeRect]; highlightView.layer.cornerRadius = 4; highlightView.layer.borderWidth = 1; highlightView.backgroundColor = [UIColor yellowColor]; highlightView.layer.borderColor = [[UIColor networkingColor] CGColor]; [_textView insertSubview:highlightView atIndex:0]; } } } - (CGRect)frameOfTextRange:(NSRange)range { self.textView.selectedRange = range; UITextRange *textRange = [self.textView selectedTextRange]; CGRect rect = [self.textView firstRectForRange:textRange]; return rect; } 

Solo tuve que hacer algo similar a esto. Suponiendo que esté usando iOS 7:

 // Build the range that you want for your text NSRange range = NSMakeRange(location, length); // Get the substring of the attributed text at that range NSAttributedString *substring = [textView.attributedText attributedSubstringFromRange:range]; // Find the frame that would enclose the substring of text. CGRect frame = [substring boundingRectWithSize:maxSize options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading) context:nil]; 

Esto debería usar el NSTextAlignment asignado a la cadena atribuida.

Como respuesta @Avt https://stackoverflow.com/a/22572201/3549781 esta pregunta. Solo estoy respondiendo por el problema de la nueva línea. Este problema de línea nueva se produce en iOS 7 + incluso si usa

 [self.textView selectedTextRange] or [self.textView positionFromPosition: offset:] 

Solo tenemos que asegurar el layout de textView antes de llamar a firstRectForRange por

 [self.textView.layoutManager ensureLayoutForTextContainer:self.textView.textContainer]; 

Cortesía: https://stackoverflow.com/a/25983067/3549781

PS: Al principio agregué esto como un comentario a la pregunta. Como la mayoría de las personas no lee comentarios, agregué esto como una respuesta.