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 (правоъгълник в точки, определящ размера и позицията). Според 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 директно, не разчитайте на контейнера за данни.
По подразбиране NSTextAttachment показва изображението в неговия оригинален размер в точки. На практика почти винаги се изисква коригиране на размера и вертикалната позиция — за това се използва свойството 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 е нула.
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.
Третият проблем — загуба на изображение при промяна на traitCollection. Когато потребителят превключи Dark Mode или Dynamic Type, размерът на шрифта може да се промени, а bounds остава същият. Решение — изчислявайте bounds динамично в метода layoutSubviews или чрез KVO на font.
Четвъртата често срещана грешка — използване на NSTextAttachment в UILabel с numberOfLines > 1. В многоредов режим и ограничена ширина TextKit коректно пренася редовете заедно с изображението. Проблемът възниква, когато височината на прикачения файл надвишава височината на реда — съседните редове се застъпват. Решение — увеличете 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 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също