ios - Color de fondo NSAttributedString y esquinas redondeadas
objective-c uiview (1)
Logré lograr el efecto anterior, así que pensé en publicar una respuesta para el mismo.
Si alguien tiene alguna sugerencia para hacer esto más efectivo, siéntase libre de contribuir. Me aseguraré de marcar su respuesta como la correcta. :)
Para hacer esto, deberá agregar un "atributo personalizado" a NSAttributedString
.
Básicamente, lo que eso significa es que puede agregar cualquier par de clave-valor, siempre que sea algo que pueda agregar a una instancia de NSDictionary
. Si el sistema no reconoce ese atributo, no hace nada. Depende de usted, como desarrollador, proporcionar una implementación personalizada y comportamiento para ese atributo.
A los efectos de esta respuesta, supongamos que he agregado un atributo personalizado llamado: @"MyRoundedBackgroundColor"
con un valor de [UIColor greenColor]
.
Para los pasos siguientes, deberá tener una comprensión básica de cómo CoreText
hace las cosas. Consulte la Guía de programación de texto principal de Apple para comprender qué es un marco / línea / ejecución de glifo / glifo, etc.
Entonces, aquí están los pasos:
- Crea una subclase UIView personalizada.
- Tener una propiedad para aceptar un
NSAttributedString
. - Cree un
CTFramesetter
utilizando esa instanciaNSAttributedString
. - Reemplazar el método
drawRect:
- Cree una instancia de
CTFrame
desdeCTFramesetter
.- Tendrá que dar un
CGPathRef
para crear elCTFrame
. Haga queCGPath
sea el mismo que el marco en el que desea dibujar el texto.
- Tendrá que dar un
- Obtenga el contexto gráfico actual y cambie el sistema de coordenadas de texto.
- Usando
CTFrameGetLines(...)
, obtenga todas las líneas en elCTFrame
que acaba de crear. - Utilizando
CTFrameGetLineOrigins(...)
, obtenga todos los orígenes de línea paraCTFrame
. - Comience un
for loop
- para cada línea en la matriz deCTLine
... - Establezca la posición del texto al comienzo de la
CTLine
usandoCGContextSetTextPosition(...)
. - Usando
CTLineGetGlyphRuns(...)
obtiene todas las carreras de glifo (CTRunRef
) deCTLine
. - Inicie otro
for loop
: para cada glyphRun en la matriz deCTRun
... - Obtenga el rango de la ejecución usando
CTRunGetStringRange(...)
. - Obtenga límites tipográficos utilizando
CTRunGetTypographicBounds(...)
. - Obtenga el desplazamiento x para la ejecución usando
CTLineGetOffsetForStringIndex(...)
. - Calcule el rect límite (llamémoslo
runBounds
) usando los valores devueltos por las funciones mencionadas anteriormente.- Recuerde:
CTRunGetTypographicBounds(...)
requiere punteros a las variables para almacenar el "ascenso" y el "descenso" del texto. Debe agregar esos para obtener la altura de ejecución.
- Recuerde:
- Obtenga los atributos para la ejecución usando
CTRunGetAttributes(...)
. - Compruebe si el diccionario de atributos contiene su atributo.
- Si su atributo existe, calcule los límites del rectángulo que debe pintarse.
- El texto principal tiene los orígenes de línea en la línea de base. Necesitamos dibujar desde el punto más bajo del texto hasta el punto más alto. Por lo tanto, tenemos que ajustarnos para el descenso.
- Por lo tanto, resta el descenso del rect límite que calculamos en el paso 16 (
runBounds
). - Ahora que tenemos los
runBounds
, sabemos qué área queremos pintar, ahora podemos usar cualquiera de los métodosUIBezierPath
/UIBezierPath
para dibujar y completar un rect con esquinas redondeadas específicas.-
UIBezierPath
tiene un método de clase de conveniencia llamadobezierPathWithRoundedRect:byRoundingCorners:cornerRadii:
que le permite redondear esquinas específicas. Usted especifica las esquinas usando máscaras de bits en el 2do parámetro.
-
- Ahora que ha llenado el rect, simplemente dibuje la ejecución del glifo usando
CTRunDraw(...)
. - Celebre la victoria por haber creado su atributo personalizado: ¡beba una cerveza o algo así! :RE
En cuanto a la detección de que el rango de atributos se extiende a varias ejecuciones, puede obtener todo el rango efectivo de su atributo personalizado cuando la primera ejecución encuentra el atributo. Si encuentra que la longitud del alcance efectivo máximo de su atributo es mayor que la longitud de su recorrido, necesita pintar esquinas agudas en el lado derecho (para un guión de izquierda a derecha). Más matemáticas le permitirán detectar el estilo de esquina más destacado para la próxima línea también. :)
Adjunto hay una captura de pantalla del efecto. El cuadro en la parte superior es una UITextView
estándar, para la cual he establecido el texto atribuido. El cuadro en la parte inferior es el que se ha implementado utilizando los pasos anteriores. La misma cadena atribuida se ha establecido para ambos textViews.
Nuevamente, si hay un enfoque mejor que el que he usado, ¡házmelo saber! :RE
Espero que esto ayude a la comunidad. :)
¡Aclamaciones!
Tengo una pregunta sobre las esquinas redondeadas y el color de fondo del texto para una UIView
personalizada.
Básicamente, necesito lograr un efecto como este (imagen adjunta - observe las esquinas redondeadas en un lado) en una UIView personalizada:
Estoy pensando que el enfoque para usar es:
- Usa el Texto central para obtener ejecuciones de glifos.
- Verifique el rango de resaltado.
- Si la ejecución actual está dentro del rango de resaltado, dibuje un rectángulo de fondo con las esquinas redondeadas y el color de relleno deseado antes de dibujar la ejecución del glifo.
- Dibuja la ejecución del glifo.
Sin embargo, no estoy seguro de si esta es la única solución (o para el caso, si esta es la solución más eficiente).
Usar UIWebView
no es una opción, así que tengo que hacerlo en una UIView
personalizada.
Mi pregunta es, ¿es este el mejor enfoque para usar, y estoy en el camino correcto? ¿O me estoy perdiendo algo importante o lo estoy haciendo mal?