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. Для зворотної сумісності завжди встановлюйте 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
}
// Відкрити екран деталей
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 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також