Obtenga índice de caracteres en UILabel desde el grifo

Me gustaría get el índice para el personaje seleccionado en un UILabel. He subclassado una UILabel. En mi awakeFromNib () tengo esto:

layoutManager = NSLayoutManager() textStorage = NSTextStorage(attributedString: self.attributedText) textStorage.addLayoutManager(layoutManager) textContainer = NSTextContainer(size: CGSizeMake(self.frame.size.width, self.frame.size.height)) textContainer.lineFragmentPadding = 0 textContainer.maximumNumberOfLines = self.numberOfLines textContainer.lineBreakMode = self.lineBreakMode textContainer.size = self.frame.size layoutManager.addTextContainer(textContainer) 

Está funcionando cómo quiero que sea para los primeros 5 caracteres de la label, como en Toco el primer carácter y en mis toques Indica obtengo un índice de 0:

var touchedCharacterIndex = layoutManager.characterIndexForPoint(touch.locationInView(self), inTextContainer: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)

Pero cuando toco en cualquier lugar más allá de los primeros cuatro caracteres de la UILabel, obtengo 4 o 0, lo cual es incorrecto.

Gracias,

Actualizar

De una sugerencia en los comentarios, agregué esto:

  func updateTextStorage() { if let attributedText = self.attributedText { textStorage = NSTextStorage(attributedString: attributedText) } textStorage?.addLayoutManager(layoutManager) textContainer = NSTextContainer(size: CGSizeMake(self.frame.size.width, self.frame.size.height)) textContainer?.lineFragmentPadding = 7 textContainer?.maximumNumberOfLines = self.numberOfLines textContainer?.lineBreakMode = self.lineBreakMode textContainer?.size = self.bounds.size layoutManager.addTextContainer(textContainer!) layoutManager.textStorage = textStorage } 

Llamo a esto en layoutSubviews() , awakFromNib y los awakFromNib de bounds , frame , attributedText y text .

Y ahora me está dando algunos índices extraños, como si la longitud del text es 21, tocar la primera letra me da 6.

TLDR: obtienes algunos índices extraños porque la cadena que ve tu layoutManager se distribuye de forma diferente a la que realmente ves en la pantalla.


Obtiene algunos índices extraños de:

 - (NSUInteger)characterIndexForPoint:(CGPoint)point inTextContainer:(NSTextContainer *)container fractionOfDistanceBetweenInsertionPoints:(nullable CGFloat *)partialFraction; 

porque su propio layoutManager debe estar sincronizado con el layoutManager interno del sistema UILabel . Lo que significa que cuando configura una fuente, tamaño, etc. en su label, el layoutManager interno layoutManager sabe porque UILabel , pero su instancia de layoutManager no lo hace.

Por lo tanto, el layout de text " visto " por su layoutManager se encuentra en una position diferente del text real representado por la label en la pantalla (fuente pnetworkingeterminada y tamaño que apuesto).

La parte complicada es mantener esas properties sincronizadas, lo que puedes hacer es establecerlas en tu text atribuido como:

 [mutableAttributedString addAttribute:NSFontAttributeName value:self.font range:NSMakeRange(0, mutableAttributedString.string.length)]; 

Y pase esta cadena atribuida a su instancia de NSTextStorage . Esto solucionó todos los problemas para mí.


INFO: para ayudarlo a depurar, puede procesar la cadena " vista " por su layoutManager usando esto:

 - (void)drawTextInRect:(CGRect)rect { [super drawTextInRect:rect]; [self.layoutManager drawGlyphsForGlyphRange:NSMakeRange(0, self.textStorage.length) atPoint:CGPointMake(0, 0)]; } 

Si ve 2 cadenas dispuestas una encima de la otra con algún problema técnico, este es su problema. ¡Si solo puedes ver uno que debería ser porque están perfectamente alineados y tu eres bueno!

Creo que el problema proviene del hecho de que NSTextContainer expone text como un UITextView, lo que significa que no estará centrado verticalmente como un UILabel.

Si ejecuta la siguiente pieza de código, puede ver dónde están desplegando sus glifos.

 var location = layoutManager.locationForGlyphAtIndex(0); 

Debería mostrar que los glifos están siendo presentados en la parte superior de su contenedor de text.

Entonces, dondequiera que llame

 var point = tapGesture.locationInView(self); 

deberá ajustarse para tener en count el cambio vertical del UILabel.