NSTextAttachment to klasa z frameworka UIKit, umożliwiająca osadzanie obrazów i innych obiektów multimedialnych w sformatowanym tekście poprzez NSAttributedString. Działa na poziomie TextKit i Core Text, dając programistom kontrolę nad rozmiarem, pozycją i zachowaniem załączników wewnątrz strumienia tekstu. Według Apple Documentation (2025), NSTextAttachment obsługuje obrazy, PDF i niestandardowe kontenery widoków, integrując się z UITextView i UILabel. Zrozumienie tego API jest niezbędne do tworzenia Rich Text w aplikacjach iOS — od czatów i edytorów po kanały informacyjne z ikonami w podpisach.
Najważniejsze
NSTextAttachment to klasa z frameworka UIKit, która pozwala osadzać obiekty multimedialne bezpośrednio w treści tekstowej NSAttributedString. W przeciwieństwie do osobnego UIImageView umieszczonego obok tekstu, NSTextAttachment sprawia, że obraz staje się częścią strumienia tekstu: obrazek przenosi się wraz z wierszami, uczestniczy w wyrównaniu i zajmuje miejsce jak zwykły znak.
Klasa jest częścią architektury TextKit, która zarządza renderowaniem tekstu w iOS i macOS. TextKit dzieli tekst na glify, uwzględnia kerning, ligatury i interlinię — a NSTextAttachment włącza się do tego potoku jako specjalny znak, który renderuje się nie jako litera, ale jako obiekt graficzny.
Każda instancja NSTextAttachment zawiera właściwości contents (dane pliku), fileType (typ UTI treści) i bounds (prostokąt w punktach określający rozmiar i położenie). Według Apple Human Interface Guidelines, bounds domyślnie równa się rozmiarowi obrazu, ale może być zmieniona dla precyzyjnego dopasowania do czcionki.
Używaj NSTextAttachment zamiast osobnego UIImageView, gdy obraz powinien zachowywać się jak część tekstu — w czatach, kanałach informacyjnych, edytorach tekstu i formularzach z ikonami w polach wprowadzania.
Podstawowy scenariusz pracy z NSTextAttachment składa się z trzech kroków: utworzyć instancję, ustawić obraz i dołączyć do NSAttributedString. Rozważmy proces na przykładzie 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
Po wywołaniu NSAttributedString(attachment:) obiekt NSTextAttachment przekształca się w atrybut NSAttachmentAttributeName, który jest dołączany do specjalnego znaku zastępczego (object replacement character, kod 0xFFFC). Ten znak nie wyświetla się jako litera — zamiast niego rysowany jest obraz z właściwości image.
Jeśli obraz powinien znajdować się w środku wiersza tekstu (np. ikona po słowie), po prostu wstaw attachmentString w odpowiednią pozycję NSMutableAttributedString. TextKit automatycznie uwzględni wysokość wiersza i wyrówna obrazek do linii bazowej.
Ważne: właściwość image jest dostępna tylko w iOS (począwszy od iOS 7). W macOS używaj właściwości contents z NSImage. Dla kompatybilności wstecznej zawsze ustawiaj image bezpośrednio, a nie polegaj na kontenerze danych.
Domyślnie NSTextAttachment wyświetla obraz w jego oryginalnym rozmiarze w punktach. W praktyce prawie zawsze wymagane jest skorygowanie rozmiaru i położenia pionowego — do tego służy właściwość bounds.
Struktura CGRect bounds zawiera origin (przesunięcie X i Y) oraz size (szerokość i wysokość). Przesunięcie Y jest szczególnie ważne: wartość ujemna opuszcza obrazek poniżej linii bazowej, dodatnia podnosi go. Typowy scenariusz — wyrównać ikonę do środka wiersza tekstu.
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
)
Obliczanie wyrównania wykorzystuje capHeight czcionki — wysokość wielkiej litery, a nie pełną wysokość wiersza. Gwarantuje to, że ikona wizualnie pokrywa się z górną granicą tekstu, a nie kołysze się między baseline a ascender. Rozmiar obrazu w przykładzie to 18×18 pt, co jest typowe dla ikon w tekście UI.
Jeśli obraz powinien być większy niż wiersz tekstu (np. podgląd zdjęcia w czacie), TextKit automatycznie zwiększy odstęp międzywierszowy dla bieżącego wiersza. W tym przypadku bounds ustawia się w naturalnym rozmiarze, a przesunięcie Y — zero.
NSTextAttachment obsługuje nie tylko PNG i JPEG, ale także PDF-dokumenty oraz dowolne pliki poprzez właściwość contents. Czyni to go uniwersalnym narzędziem do Rich Text w aplikacjach iOS.
Przy ustawianiu obrazu PDF w attachment.image system automatycznie renderuje pierwszą stronę dokumentu. Jednak dla dokładnego wyświetlania używaj danych bezpośrednio przez inicjalizator z data i typem 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)
Parametr ofType przyjmuje ciąg UTI (Uniform Type Identifier). Standardowe typy: public.image (dowolny obraz), public.jpeg, public.png, com.adobe.pdf. Przy podaniu poprawnego UTI system wybiera odpowiedni sposób renderowania — dla PDF jest to CGPDFDocument, dla obrazów — CGImageSource.
Dla niestandardowych typów plików (np. grafika wektorowa w formacie SVG) potrzebna będzie podklasa NSTextAttachment nadpisująca metodę image(forBounds:textContainer:characterIndex:). Jeśli standardowe renderowanie nie pasuje, zwróć nil i zaimplementuj rysowanie przez Core Graphics w metodzie draw.
NSTextAttachment działa poprawnie zarówno w UILabel, jak i w UITextView. Jednak UITextView zapewnia więcej możliwości: interakcję z załącznikami (dotknięcie ikony), edycję tekstu wraz z obrazkami oraz obsługę NSAttachmentBehavior.
Do obsługi dotknięcia NSTextAttachment w UITextView używa się delegata UITextViewDelegate i metody textView(_:shouldInteractWith:in:interaction:). Ta metoda jest wywoływana, gdy użytkownik dotknie załącznika, i pozwala przejść do ekranu, otworzyć popup lub odtworzyć animację.
func textView(_ textView: UITextView,
shouldInteractWith attachment: NSTextAttachment,
in characterRange: NSRange,
interaction: UITextItemInteraction) -> Bool {
if interaction == .preview {
return false
}
// Otwórz ekran szczegółów
showImageDetail(attachment.image)
return false
}
Metoda rozróżnia typy interakcji przez parametr interaction: .preview (3D Touch / Haptic Touch), .default (zwykłe dotknięcie) i .presentActions (menu kontekstowe). Dla każdego typu można ustawić własne zachowanie lub zabronić go, zwracając false.
Przy edycji UITextView z NSTextAttachment ważne jest: usunięcie znaku zastępczego (0xFFFC) usuwa również załącznik. Użytkownik widzi obrazek jako jeden element — zaznacza się on w całości, a nie piksel po pikselu. Do obsługi przeciągania załączników w iOS 15+ używaj NSTextAttachmentViewProvider.
Pierwszy częsty błąd — ignorowanie bounds. Programiści często polegają na oryginalnym rozmiarze obrazu, co prowadzi do gigantycznych ikon w tekście lub odwrotnie — do obrazków, które są ledwo widoczne. Zawsze ustawiaj bounds jawnie z uwzględnieniem czcionki i kontekstu.
Drugi błąd — nieprawidłowe przesunięcie Y. Dodatnia wartość w bounds.origin.y opuszcza obrazek (w układzie współrzędnych UIKit oś Y skierowana jest w dół — wydaje się to nielogiczne, ale tak działa Core Graphics). Do wyrównania do środka wiersza używaj formuły z capHeight czcionki, jak pokazano w sekcji 3.
Trzeci problem — utrata obrazu przy zmianie traitCollection. Gdy użytkownik przełącza Dark Mode lub Dynamic Type, rozmiar czcionki może się zmienić, a bounds pozostaje taki sam. Rozwiązanie — obliczać bounds dynamicznie w metodzie layoutSubviews lub przez KVO na font.
Czwarty częsty błąd — używanie NSTextAttachment w UILabel z numberOfLines > 1. W trybie wielowierszowym i ograniczonej szerokości TextKit poprawnie przenosi wiersze wraz z obrazem. Problem pojawia się, gdy wysokość attachment przekracza wysokość wiersza — sąsiednie wiersze nachodzą na siebie. Rozwiązanie — zwiększyć lineSpacing przez NSMutableParagraphStyle.
Często zadawane pytania
NSTextAttachment sprawia, że obraz staje się częścią strumienia tekstu: przenosi się, wyrównuje i skaluje wraz z tekstem. UIImageView jest przypisany do współrzędnych nadrzędnego widoku i nie uczestniczy w układzie tekstu.
Tak, jeśli ustawisz właściwość attributedText zamiast text. UILabel poprawnie wyświetla NSTextAttachment, ale nie obsługuje interaktywności. Do dotykania załączników używaj UITextView.
Zmień właściwość bounds istniejącej instancji NSTextAttachment. Tekst automatycznie przerenderuje się z nowymi rozmiarami bez konieczności tworzenia nowego NSAttributedString.
NSTextAttachment obsługuje statyczne obrazy. Dla GIF i animowanych formatów potrzebna będzie niestandardowa implementacja przez NSTextAttachmentViewProvider w iOS 15+ lub przez CADisplayLink.
Utwórz instancję NSTextAttachment, ustaw image lub data, ustaw bounds, owiń w NSAttributedString(attachment:) i wstaw do NSMutableAttributedString przez metodę insert(_:at:).
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również