NSTextAttachment es una clase del framework UIKit que permite incrustar imágenes y otros objetos multimedia en texto formateado a través de NSAttributedString. Funciona a nivel de TextKit y Core Text, dando a los desarrolladores control sobre el tamaño, la posición y el comportamiento de los attachments dentro del flujo de texto. Según Apple Documentation (2025), NSTextAttachment admite imágenes, PDF y contenedores de vista personalizados, integrándose con UITextView y UILabel. Comprender esta API es necesario para crear Rich Text en aplicaciones iOS, desde chats y editores hasta feeds de noticias con iconos en los pies de foto.
Puntos clave
NSTextAttachment es una clase del framework UIKit que permite incrustar objetos multimedia directamente en el contenido de texto de NSAttributedString. A diferencia de un UIImageView separado colocado junto al texto, NSTextAttachment hace que la imagen sea parte del flujo de texto: la imagen se ajusta con las líneas, participa en la alineación y ocupa espacio como un carácter normal.
La clase es parte de la arquitectura TextKit, que gestiona la representación del texto en iOS y macOS. TextKit divide el texto en glifos, tiene en cuenta el kerning, las ligaduras y el interlineado, y NSTextAttachment se integra en este pipeline como un carácter especial que se renderiza no como una letra sino como un objeto gráfico.
Cada instancia de NSTextAttachment contiene contents (datos del archivo), fileType (tipo de contenido UTI) y bounds (un rectángulo en points que define el tamaño y la posición). Según Apple Human Interface Guidelines, bounds por defecto es el tamaño de la imagen, pero se puede cambiar para un ajuste preciso con la fuente.
Use NSTextAttachment en lugar de un UIImageView separado cuando la imagen deba comportarse como parte del texto, en chats, feeds de noticias, editores de texto y formularios con iconos en campos de entrada.
El flujo de trabajo básico con NSTextAttachment consta de tres pasos: crear una instancia, establecer la imagen y adjuntarla a NSAttributedString. Veamos el proceso usando Swift.
let attachment = NSTextAttachment()
attachment.image = UIImage(named: "icon-star")
let attachmentString = NSAttributedString(attachment: attachment)
let text = NSMutableAttributedString(string: "Rating: ")
text.append(attachmentString)
let label = UILabel()
label.attributedText = text
Después de llamar a NSAttributedString(attachment:), el objeto NSTextAttachment se convierte en el atributo NSAttachmentAttributeName, que se adjunta a un carácter especial de reemplazo de objeto (código 0xFFFC). Este carácter no se muestra como una letra; en su lugar, se dibuja la imagen de la propiedad image.
Si la imagen debe colocarse en medio de una línea de texto (por ejemplo, un icono después de una palabra), simplemente inserte attachmentString en la posición deseada en NSMutableAttributedString. TextKit tendrá en cuenta automáticamente la altura de la línea y alineará la imagen con la línea base.
Importante: la propiedad image solo está disponible en iOS (desde iOS 7). En macOS, use la propiedad contents con NSImage. Para compatibilidad con versiones anteriores, siempre prefiera establecer image directamente en lugar de depender del contenedor de datos.
Por defecto, NSTextAttachment muestra la imagen en su tamaño original en points. En la práctica, casi siempre es necesario ajustar el tamaño y la posición vertical, lo que se hace mediante la propiedad bounds.
La estructura CGRect bounds incluye origin (desplazamiento en X e Y) y size (ancho y alto). El desplazamiento en Y es particularmente importante: un valor negativo baja la imagen por debajo de la línea base, un valor positivo la sube. Un escenario típico es alinear un icono al centro de una línea de texto.
let attachment = NSTextAttachment()
attachment.image = UIImage(named: "icon-star")
let font = UIFont.systemFont(ofSize: 16)
let fontCapHeight = font.capHeight
let imageSize = CGSize(width: 18, height: 18)
attachment.bounds = CGRect(
x: 0,
y: (fontCapHeight - imageSize.height) / 2,
width: imageSize.width,
height: imageSize.height
)
El cálculo de alineación utiliza el capHeight de la fuente, la altura de una letra mayúscula, no la altura completa de la línea. Esto garantiza que el icono coincida visualmente con el borde superior del texto en lugar de flotar entre la línea base y el ascendente. El tamaño de la imagen en el ejemplo es de 18×18 pt, que es típico para iconos en texto de UI.
Si la imagen debe ser más grande que la línea de texto (por ejemplo, una vista previa de foto en un chat), TextKit aumentará automáticamente el interlineado para la línea actual. En este caso, bounds debe establecerse al tamaño natural y el desplazamiento en Y a cero.
NSTextAttachment admite no solo PNG y JPEG, sino también documentos PDF, así como archivos arbitrarios a través de la propiedad contents. Esto lo convierte en una herramienta universal para Rich Text en aplicaciones iOS.
Al establecer una imagen PDF en attachment.image, el sistema renderiza automáticamente la primera página del documento. Sin embargo, para una representación precisa, use los datos directamente a través del inicializador con datos y tipo UTI:
guard let pdfData = Bundle.main.url(forResource: "document",
withExtension: "pdf") else { return }
let fileData = Data(contentsOf: pdfData)
let attachment = NSTextAttachment(data: fileData,
ofType: "com.adobe.pdf")
attachment.bounds = CGRect(x: 0, y: -4,
width: 24, height: 24)
let pdfString = NSAttributedString(attachment: attachment)
El parámetro ofType acepta una cadena UTI (Uniform Type Identifier). Los tipos estándar incluyen: public.image (cualquier imagen), public.jpeg, public.png, com.adobe.pdf. Al especificar la UTI correcta, el sistema selecciona el método de renderizado apropiado: para PDF es CGPDFDocument, para imágenes es CGImageSource.
Para tipos de archivo personalizados (por ejemplo, gráficos vectoriales en formato SVG), necesitará una subclase de NSTextAttachment que sobrescriba el método image(forBounds:textContainer:characterIndex:). Si la representación estándar no es adecuada, devuelva nil e implemente el dibujo a través de Core Graphics en el método draw.
NSTextAttachment funciona correctamente tanto en UILabel como en UITextView. Sin embargo, UITextView proporciona más capacidades: interacción con attachments (tocar un icono), edición de texto junto con imágenes y soporte para NSAttachmentBehavior.
Para manejar toques en NSTextAttachment en UITextView, use el protocolo UITextViewDelegate y el método textView(_:shouldInteractWith:in:interaction:). Este método se llama cuando el usuario toca un attachment y permite navegar a una pantalla, abrir un popup o reproducir una animación.
func textView(_ textView: UITextView,
shouldInteractWith attachment: NSTextAttachment,
in characterRange: NSRange,
interaction: UITextItemInteraction) -> Bool {
if interaction == .preview {
return false
}
// Abrir pantalla de detalles
showImageDetail(attachment.image)
return false
}
El método distingue los tipos de interacción a través del parámetro interaction: .preview (3D Touch / Haptic Touch), .default (toque normal) y .presentActions (menú contextual). Para cada tipo, puede definir su propio comportamiento o deshabilitarlo devolviendo false.
Al editar UITextView con NSTextAttachment, es importante recordar: eliminar el carácter de reemplazo (0xFFFC) también elimina el attachment. El usuario ve la imagen como un solo elemento: se selecciona en su conjunto, no píxel por píxel. Para soporte de arrastrar y soltar attachments en iOS 15+, use NSTextAttachmentViewProvider.
El primer error común es ignorar bounds. Los desarrolladores a menudo confían en el tamaño original de la imagen, lo que lleva a iconos gigantes en el texto o, por el contrario, a imágenes que apenas se ven. Siempre establezca bounds explícitamente teniendo en cuenta la fuente y el contexto.
El segundo error es desplazamiento incorrecto en Y. Un valor positivo en bounds.origin.y baja la imagen (en el sistema de coordenadas de UIKit, el eje Y apunta hacia abajo, lo que parece contradictorio, pero así funciona Core Graphics). Para alinear al centro dentro de una línea, use la fórmula con capHeight de la fuente como se muestra en la Sección 3.
El tercer problema es pérdida de imagen al cambiar traitCollection. Cuando el usuario cambia el Modo Oscuro o Dynamic Type, el tamaño de la fuente puede cambiar, pero bounds permanece igual. La solución es calcular bounds dinámicamente en el método layoutSubviews o mediante KVO en font.
El cuarto error frecuente es usar NSTextAttachment en UILabel con numberOfLines > 1. En modo multilínea con ancho limitado, TextKit ajusta correctamente las líneas junto con la imagen. El problema surge cuando la altura del attachment supera la altura de la línea: las líneas adyacentes se superponen. La solución es aumentar lineSpacing mediante NSMutableParagraphStyle.
Preguntas frecuentes
NSTextAttachment hace que la imagen sea parte del flujo de texto: se ajusta, alinea y escala con el texto. UIImageView está vinculado a las coordenadas de la superview y no participa en el diseño del texto.
Sí, si se establece la propiedad attributedText en lugar de text. UILabel muestra correctamente NSTextAttachment pero no admite interactividad. Para toques en el attachment, use UITextView.
Cambie la propiedad bounds de la instancia existente de NSTextAttachment. El texto se volverá a renderizar automáticamente con los nuevos tamaños sin necesidad de crear un nuevo NSAttributedString.
NSTextAttachment admite imágenes estáticas. Para GIF y formatos animados, se requiere una implementación personalizada mediante NSTextAttachmentViewProvider en iOS 15+ o mediante CADisplayLink.
Cree una instancia de NSTextAttachment, establezca la imagen o los datos, configure bounds, envuélvalo en NSAttributedString(attachment:) e insértelo en NSMutableAttributedString usando el método insert(_:at:).
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también