NSTextAttachment — это класс из фреймворка UIKit, позволяющий встраивать изображения и другие медиа-объекты в форматированный текст через NSAttributedString. Он работает на уровне TextKit и Core Text, давая разработчикам контроль над размером, позицией и поведением вложений внутри текстового потока. По данным Apple Documentation (2025), NSTextAttachment поддерживает изображения, PDF и кастомные вью-контейнеры, интегрируясь с UITextView и UILabel. Понимание этого API необходимо для создания Rich Text в iOS-приложениях — от чатов и редакторов до лент новостей с иконками в подписях.
Главное
NSTextAttachment — это класс из фреймворка UIKit, который позволяет встраивать медиа-объекты непосредственно в текстовое содержимое NSAttributedString. В отличие от отдельного UIImageView, расположенного рядом с текстом, NSTextAttachment делает изображение частью текстового потока: картинка переносится вместе со строками, участвует в выравнивании и занимает место как обычный символ.
Класс является частью архитектуры TextKit, которая управляет отрисовкой текста на iOS и macOS. TextKit разбивает текст на глифы, учитывает кернинг, лигатуры и интерлиньяж — и NSTextAttachment встраивается в этот конвейер как специальный символ, который рендерится не как буква, а как графический объект.
Каждый экземпляр NSTextAttachment содержит свойство contents (данные файла), fileType (UTI-тип контента) и bounds (прямоугольник в points, определяющий размер и положение). По данным Apple Human Interface Guidelines, bounds по умолчанию равен размеру изображения, но может быть изменён для точной подгонки под шрифт.
Используйте NSTextAttachment вместо отдельного UIImageView, когда изображение должно вести себя как часть текста — в чатах, новостных лентах, текстовых редакторах и формах с иконками в полях ввода.
Базовый сценарий работы с NSTextAttachment состоит из трёх шагов: создать экземпляр, задать изображение и прикрепить к NSAttributedString. Рассмотрим процесс на примере 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
После вызова NSAttributedString(attachment:) объект NSTextAttachment преобразуется в атрибут NSAttachmentAttributeName, который прикрепляется к специальному символу-заменителю (object replacement character, код 0xFFFC). Этот символ не отображается как буква — вместо него рисуется изображение из свойства image.
Если изображение должно располагаться в середине строки текста (например, иконка после слова), просто вставьте attachmentString в нужную позицию NSMutableAttributedString. TextKit автоматически учтёт высоту строки и выровняет картинку по базовой линии.
Важно: свойство image доступно только на iOS (начиная с iOS 7). На macOS используйте свойство contents с NSImage. Для обратной совместимости preferred всегда устанавливать image напрямую, а не полагаться на data-контейнер.
По умолчанию NSTextAttachment отображает изображение в его оригинальном размере в points. На практике почти всегда требуется скорректировать размер и вертикальное положение — для этого используется свойство bounds.
Структура CGRect bounds включает origin (смещение по X и Y) и size (ширина и высота). Смещение по Y особенно важно: отрицательное значение опускает картинку ниже базовой линии, положительное — поднимает. Типичный сценарий — выровнять иконку по центру строки текста.
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
)
Расчёт выравнивания использует capHeight шрифта — высоту заглавной буквы, а не полную высоту строки. Это гарантирует, что иконка визуально совпадает с верхней границей текста, а не болтается между baseline и ascender. Размер изображения в примере — 18×18 pt, что типично для иконок в UI-тексте.
Если изображение должно быть больше строки текста (например, превью фотографии в чате), TextKit автоматически увеличит межстрочный интервал для текущей строки. В этом случае bounds задаётся в натуральную величину, а смещение по Y — zero.
NSTextAttachment поддерживает не только PNG и JPEG, но и PDF-документы, а также произвольные файлы через свойство contents. Это делает его универсальным инструментом для Rich Text в iOS-приложениях.
При установке PDF-изображения в attachment.image система автоматически рендерит первую страницу документа. Однако для точного отображения используйте данные напрямую через инициализатор с data и 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)
Параметр ofType принимает UTI-строку (Uniform Type Identifier). Стандартные типы: public.image (любое изображение), public.jpeg, public.png, com.adobe.pdf. При указании корректного UTI система выбирает правильный способ рендеринга — для PDF это CGPDFDocument, для изображений — CGImageSource.
Для кастомных типов файлов (например, векторная графика в формате SVG) потребуется подкласс NSTextAttachment, переопределяющий метод image(forBounds:textContainer:characterIndex:). Если стандартная отрисовка не подходит, верните nil и реализуйте рисование через Core Graphics в методе draw.
NSTextAttachment корректно работает как в UILabel, так и в UITextView. Однако UITextView предоставляет больше возможностей: взаимодействие с вложениями (тап по иконке), редактирование текста вместе с картинками и поддержка NSAttachmentBehavior.
Для обработки тапа по NSTextAttachment в UITextView используется делегат UITextViewDelegate и метод textView(_:shouldInteractWith:in:interaction:). Этот метод вызывается, когда пользователь касается вложения, и позволяет выполнить переход на экран, открыть попап или воспроизвести анимацию.
func textView(_ textView: UITextView,
shouldInteractWith attachment: NSTextAttachment,
in characterRange: NSRange,
interaction: UITextItemInteraction) -> Bool {
if interaction == .preview {
return false
}
// Open detail screen
showImageDetail(attachment.image)
return false
}
Метод различает типы взаимодействия через параметр interaction: .preview (3D Touch / Haptic Touch), .default (обычный тап) и .presentActions (контекстное меню). Для каждого типа можно задать своё поведение или запретить его, вернув false.
При редактировании UITextView с NSTextAttachment важно помнить: удаление символа-заменителя (0xFFFC) удаляет и вложение. Пользователь видит картинку как единый элемент — она выделяется целиком, а не по пикселям. Для поддержки перетаскивания вложений в iOS 15+ используйте NSTextAttachmentViewProvider.
Первая распространённая ошибка — игнорирование bounds. Разработчики часто полагаются на оригинальный размер изображения, что приводит к гигантским иконкам в тексте или, наоборот, к картинкам, которые едва видны. Всегда устанавливайте bounds явно с учётом шрифта и контекста.
Вторая ошибка — неправильное смещение по Y. Положительное значение в bounds.origin.y опускает картинку (в системе координат UIKit ось Y направлена вниз — это кажется нелогичным, но так работает Core Graphics). Для выравнивания по центру строки используйте формулу с capHeight шрифта, как показано в разделе 3.
Третья проблема — потеря изображения при change of traitCollection. Когда пользователь переключает Dark Mode или Dynamic Type, размер шрифта может измениться, а bounds останется прежним. Решение — вычислять bounds динамически в методе layoutSubviews или через KVO на font.
Четвёртая частая ошибка — использование NSTextAttachment в UILabel с numberOfLines > 1. При многострочном режиме и ограниченной ширине TextKit корректно переносит строки вместе с изображением. Проблема возникает, когда высота attachment превышает высоту строки — соседние строки наезжают друг на друга. Решение — увеличить lineSpacing через NSMutableParagraphStyle.
Часто задаваемые вопросы
NSTextAttachment делает изображение частью текстового потока: оно переносится, выравнивается и масштабируется вместе с текстом. UIImageView привязан к координатам супервью и не участвует в вёрстке текста.
Да, если установить свойство attributedText вместо text. UILabel корректно отображает NSTextAttachment, но не поддерживает интерактив. Для тапов по вложению используйте UITextView.
Измените свойство bounds существующего экземпляра NSTextAttachment. Текст автоматически перерендерится с новыми размерами без необходимости создавать новый NSAttributedString.
NSTextAttachment поддерживает статические изображения. Для GIF и анимированных форматов потребуется кастомная реализация через NSTextAttachmentViewProvider на iOS 15+ или через CADisplayLink.
Создайте экземпляр NSTextAttachment, установите image или data, задайте bounds, оберните в NSAttributedString(attachment:) и вставьте в NSMutableAttributedString через метод insert(_:at:).
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также