← Wszystkie wpisy

Genmoji i NSAdaptiveImageGlyph: emoji w treści tekstu na iOS 18+

Genmoji to nazwa funkcji Apple Intelligence widoczna dla użytkownika, służąca do tworzenia własnych emoji: użytkownik wpisuje opis, system generuje obraz w stylu emoji osadzony w treści, a wynik pojawia się w tekście obok standardowych emoji Unicode. Powierzchnią widoczną dla deweloperów jest NSAdaptiveImageGlyph — klasa dostępna od iOS 18+, która reprezentuje adaptacyjne obrazy osadzone w tekście atrybutowanym1. Genmoji jest jednym ze źródeł instancji NSAdaptiveImageGlyph; aplikacje obsługujące tekst atrybutowany muszą wspierać tę klasę, aby wyświetlać Genmoji (oraz wszelkie przyszłe treści typu adaptive image glyph, które Apple może wprowadzić).

TL;DR

  • NSAdaptiveImageGlyph (iOS 18+) to typ danych, który opakowuje obraz adaptacyjny wraz z identyfikującymi metadanymi. Genmoji wprowadzane z klawiatury systemowej trafia jako instancje NSAdaptiveImageGlyph osadzone w tekście atrybutowanym2.
  • supportsAdaptiveImageGlyph jest zadeklarowane w protokole UITextInput; UITextView jest zgodne z tym protokołem, więc właściwość można ustawić jako textView.supportsAdaptiveImageGlyph = true. Apple opisuje to wprost: gdy właściwość ma wartość false, „system wprowadzania nie pozwala, aby pole tekstowe zawierało obrazy adaptacyjne”. Aplikacje muszą włączyć tę opcję, aby Genmoji się wyświetlało.
  • Adaptive image glyphs wymagają TextKit 2. Aplikacje wciąż używające TextKit 1 nie renderują NSAdaptiveImageGlyph poprawnie. Nowe aplikacje kierowane na iOS 18+ powinny domyślnie korzystać z TextKit 2.
  • NSAttributedString przenosi adaptive image glyphs przez atrybut NSAttributedString.Key.adaptiveImageGlyph. Inicjator NSAttributedString(adaptiveImageGlyph:attributes:) tworzy ciąg atrybutowany zawierający pojedynczy glif3.
  • Trwałość i zachowanie danych w obie strony wymagają uwagi. Przechowywanie jako zwykły tekst całkowicie usuwa Genmoji; formaty tekstu sformatowanego (RTFD, Markdown z rozszerzeniami, HTML z osadzonymi danymi obrazu) zachowują je.

Co przenosi NSAdaptiveImageGlyph

NSAdaptiveImageGlyph to opakowanie danych z czterema właściwościami identyfikującymi2:

  • imageContent: Data. Same dane obrazu, w formacie zadeklarowanym przez contentType.
  • contentIdentifier: String. Unikalny identyfikator instancji glifu. Używany do deduplikacji oraz do wewnętrznego cache’owania systemu.
  • contentDescription: String. Tekst alternatywny opisujący glif. Korzystają z niego aplikacje udostępniające etykiety dostępności lub wysyłające glify do odbiorców niewspierających tej funkcji.
  • contentType: UTType. Właściwość typu na poziomie klasy, ujawniająca format obrazu, którego Apple używa dla adaptacyjnych glifów (wariant HEIC). Aplikacje serializujące dane sprawdzają tę wartość, aby zastosować obsługę zależną od formatu.

Dane mają zwykle kilkadziesiąt kilobajtów dla standardowego Genmoji. Wiele rozmiarów jest zakodowanych w tym samym pliku obrazu z wykorzystaniem funkcji adaptacyjnych obrazów HEIC; system wybiera odpowiedni rozmiar w zależności od kontekstu renderowania.

Włączanie adaptive image glyphs w UITextView

Włączenie sprowadza się do jednej właściwości1:

import UIKit

let textView = UITextView()
textView.supportsAdaptiveImageGlyph = true
// TextKit 2 is required; modern UITextView usage gets it by default
// (see: developer.apple.com/documentation/uikit/using-textkit-2-to-interact-with-text)

Bez supportsAdaptiveImageGlyph = true Genmoji wpisane przez użytkownika pojawia się jako znak zastępczy (system nie potrafi wyrenderować glifu). Ustawienie tej właściwości włącza zarówno renderowanie, jak i zakładkę „Genmoji” w klawiaturze systemowej, co umożliwia użytkownikowi tworzenie własnych Genmoji w obrębie pola tekstowego.

Natywne TextField i TextEditor w SwiftUI nie udostępniają obecnie modyfikatora supportsAdaptiveImageGlyph. Aplikacje SwiftUI, które potrzebują renderowania adaptive image glyphs, opakowują UITextView w UIViewRepresentable i ustawiają supportsAdaptiveImageGlyph = true w widoku bazowym. Społecznościowe wrappery, takie jak GlyphMeThat, dostarczają gotowy pomost.

TextKit 2 jest niezbędny

NSAdaptiveImageGlyph wymaga architektury układu z TextKit 24. TextKit 1 (starszy silnik tekstu, dostarczany z oryginalnym modelem NSTextStorage/NSLayoutManager/NSTextContainer) nie renderuje adaptive image glyphs poprawnie; glif pojawia się jako ogólny znak zastępczy lub w ogóle się nie układa.

Aplikacje znajdują się w jednym z trzech stanów:

Nowe aplikacje na iOS 18+. Domyślnie TextKit 2. UITextView zainicjowany przez Interface Builder lub init(frame:textContainer:) używa TextKit 2 domyślnie od iOS 16+4. Nowy kod dostaje to za darmo.

Starsze aplikacje wciąż używające TextKit 1. Wymagana jest migracja. Migracja do TextKit 2 nie jest trywialna dla aplikacji, które dziedziczą po NSLayoutManager, nadpisują metody delegata związane z układem lub korzystają bezpośrednio ze starszego NSTextStorage. Dokumentacja TextKit 2 firmy Apple opisuje nowoczesną architekturę układu; w aplikacjach o prostym wykorzystaniu UITextView migracja jest w większości automatyczna.

Aplikacje hybrydowe. Niektóre aplikacje osadzają WKWebView do edycji HTML obok UITextView do edycji zwykłego tekstu. WKWebView obsługuje adaptive image glyphs przez własną ścieżkę renderowania (nie TextKit), więc aplikacja hybrydowa może mieć jedną powierzchnię, która wspiera Genmoji, oraz drugą, która je usuwa. Należy udokumentować takie zachowanie; użytkownicy zauważają, gdy jeden edytor obsługuje własne emoji, a drugi je odrzuca.

Integracja z NSAttributedString

Adaptive image glyphs przepływają przez ciągi atrybutowane za pośrednictwem atrybutu NSAttributedString.Key.adaptiveImageGlyph3:

import UIKit

// Construct an attributed string containing a single adaptive image glyph
let glyph: NSAdaptiveImageGlyph = ...
let attrString = NSAttributedString(
    adaptiveImageGlyph: glyph,
    attributes: [
        .font: UIFont.systemFont(ofSize: 17)
    ]
)

// Concatenate with surrounding text
let composed = NSMutableAttributedString(string: "Look at this ")
composed.append(attrString)
composed.append(NSAttributedString(string: " I just made!"))

Ten wzorzec składa się: glif w tekście w jeszcze dłuższym tekście. System obsługuje układ (w tym adaptacyjne dopasowanie rozmiaru glifu do czcionki otaczającego tekstu) automatycznie.

Przy odczycie iteracja po atrybucie .adaptiveImageGlyph w NSAttributedString zwraca instancje NSAdaptiveImageGlyph w pozycjach, w których się pojawiają:

attributedString.enumerateAttribute(
    .adaptiveImageGlyph,
    in: NSRange(location: 0, length: attributedString.length)
) { value, range, _ in
    if let glyph = value as? NSAdaptiveImageGlyph {
        // process glyph + range
    }
}

Aplikacje, które filtrują, transformują lub utrwalają tekst, korzystają z tej enumeracji, aby znaleźć glify i zdecydować, co z nimi zrobić.

Trwałość i serializacja

Przechowywanie jako zwykły tekst (String, plik UTF-8) nie zachowuje adaptive image glyphs. Znak Unicode reprezentujący glif w tekście atrybutowanym zostaje zserializowany jako U+FFFC (object replacement) lub nic, a same dane glifu zostają utracone.

Aby zachowanie w obie strony było możliwe, aplikacje potrzebują formatu tekstu sformatowanego5:

RTFD. Format tekstu sformatowanego firmy Apple z załącznikami. Zachowuje adaptive image glyphs w obie strony. Używany przez Notatki, Mail (przy wysyłaniu treści sformatowanych) oraz TextEdit. Format jest rozwlekły (pakiet katalogów z załącznikami), ale bezstratny.

HTML z osadzonymi obrazami. Przyjazny dla webu. Glify są serializowane jako tagi <img> z danymi w formacie data URI zakodowanymi w base64. Większy rozmiar danych, ale działa u większości odbiorców obsługujących tekst sformatowany.

Markdown z rozszerzeniami. Standardowy Markdown nie ma składni dla adaptive image glyph, ale rozszerzone dialekty (CommonMark z obsługą załączników, własny rozszerzony Markdown firmy Apple) potrafią je przenosić. Należy udokumentować wymagany dialekt dla każdej trwałości opartej na markdown.

Aplikacje wysyłające tekst przez sieć (czat, e-mail, media społecznościowe) muszą zdecydować: zachować glify od końca do końca (działa tylko, jeśli zarówno nadawca, jak i odbiorca korzystają z iOS 18+, a transport obsługuje tekst sformatowany), usunąć glify i podstawić tekst zastępczy (contentDescription) lub wyrenderować glif jako obraz systemowy i osadzić ten obraz. Właściwy wybór zależy od odbiorców i platformy.

Częste błędy

Trzy tryby awarii powtarzają się przy wdrażaniu NSAdaptiveImageGlyph:

Pominięcie supportsAdaptiveImageGlyph = true. Najczęstszy błąd. Pole tekstowe renderuje emoji Unicode poprawnie, ale Genmoji pojawia się jako znaki zastępcze. Rozwiązanie: ustawić właściwość na true w każdym UITextView (UIKit) lub w dowolnym typie zgodnym z NSTextInputClient, takim jak NSTextView (AppKit). W SwiftUI nie istnieje natywny modyfikator; należy opakować UITextView w UIViewRepresentable (lub NSViewRepresentable na macOS) i ustawić właściwość w widoku bazowym.

Trwałość w zwykłym tekście usuwająca glify. Zapis treści pola tekstowego jako text (zwykły String) odrzuca Genmoji. Użytkownik wpisuje własne emoji, widzi je w polu tekstowym, zapisuje dokument, otwiera ponownie — emoji znika. Rozwiązanie: utrwalać jako attributedText w formacie tekstu sformatowanego, który obsługuje adaptive image glyphs (RTFD, HTML, własny format z bocznym kanałem na załączniki).

Transmisja sieciowa cicho gubiąca glify. Aplikacja do wiadomości serializująca komunikaty wychodzące jako zwykły tekst usuwa Genmoji przy wysyłaniu. Odbiorca widzi znak zastępczy lub puste miejsce. Rozwiązanie: albo wysyłać treść sformatowaną (i upewnić się, że odbiorca ją obsługuje), albo zastępować contentDescription dla odbiorców obsługujących tylko zwykły tekst i dołączać dane obrazu jako oddzielny załącznik.

Co ten wzorzec oznacza dla aplikacji na iOS 18+

Trzy wnioski.

  1. Należy ustawić supportsAdaptiveImageGlyph = true na każdym polu tekstowym. Aplikacje przyjmujące tekst od użytkowników domyślnie włączają adaptive image glyphs. Ta jedna właściwość decyduje o tym, czy Genmoji się renderuje, czy się psuje.

  2. Należy migrować do TextKit 2, jeśli pozostajecie Państwo przy TextKit 1. TextKit 1 jest w trybie utrzymaniowym. Nowe funkcje ery iOS 26 (adaptive image glyphs, inline rewrite Writing Tools, renderowanie tekstu Liquid Glass) zakładają TextKit 2. Koszt migracji jest realny, ale alternatywą jest pozostanie w przestarzałym silniku tekstu.

  3. Należy wybrać format trwałości z myślą o adaptive image glyphs. RTFD do natywnej trwałości iOS; HTML z osadzonymi obrazami do trwałości kompatybilnej z webem; własny format binarny z bocznym kanałem na załączniki do aplikacji o wysokiej wydajności. Zwykły tekst jest niewłaściwym ustawieniem domyślnym dla aplikacji, w których użytkownicy będą wpisywać Genmoji.

Pełny klaster Apple Ecosystem: Writing Tools jako równoległa powierzchnia tekstowa Apple Intelligence; Image Playground do generowania obrazów; App Intents 2.0 dla integracji Apple Intelligence w iOS 26; Foundation Models dla LLM działającego na urządzeniu. Hub znajduje się w Apple Ecosystem Series. Szerszy kontekst dotyczący iOS i agentów AI opisuje iOS Agent Development guide.

FAQ

Czy moja aplikacja dostaje Genmoji „za darmo”, jeśli używa po prostu UITextView?

Niezupełnie. Domyślną wartością UITextView.supportsAdaptiveImageGlyph jest false. Aplikacje muszą włączyć tę opcję, ustawiając właściwość na true. Po włączeniu zakładka Genmoji w klawiaturze systemowej pojawia się dla użytkownika, a wklejone Genmoji renderuje się poprawnie. Bez tego włączenia Genmoji wpisane gdzie indziej i wklejone do pola tekstowego pojawia się jako znaki zastępcze.

Czy potrzebuję włączonego Apple Intelligence, aby testować Genmoji?

Do pełnego tworzenia Genmoji — tak. Proces tworzenia Genmoji widoczny dla użytkownika wymaga sprzętu obsługującego Apple Intelligence (iPhone 15 Pro i nowszy, komputery Mac z układami serii M) z iOS 18+ i włączonym Apple Intelligence. Do testów deweloperskich renderowania NSAdaptiveImageGlyph można konstruować testowe instancje glifów programowo z przykładowymi danymi obrazu i weryfikować renderowanie pola tekstowego na dowolnym urządzeniu lub symulatorze z iOS 18+.

Co dzieje się z Genmoji, gdy wysyłam je do osoby korzystającej z iOS 17?

Bez transportu obsługującego tekst sformatowany, który zachowuje glif, odbiorca widzi contentDescription (tekst alternatywny) lub znak zastępczy. Nowoczesne frameworki komunikatorów (aplikacja Wiadomości firmy Apple, najnowsze wersje klientów pocztowych) obsługują ten fallback automatycznie; własne protokoły wymagają jawnej obsługi.

Czy mogę tworzyć instancje NSAdaptiveImageGlyph programowo?

Tak. Publiczny inicjator to init(imageContent: Data), który przyjmuje wstępnie zakodowane dane HEIC adaptive-image. contentIdentifier i contentDescription to właściwości instancji odczytywane z zakodowanego ładunku; contentType jest właściwością UTType na poziomie klasy, opisującą format używany przez Apple dla adaptacyjnych glifów (wariant HEIC), a nie wartością specyficzną dla instancji. Aplikacje tworzące własne adaptive image glyphs przygotowują ładunek HEIC z osadzonym identyfikatorem i opisem dla danego glifu, a następnie konstruują glif z tych danych. Sesja WWDC 2024 nr 10220 („Bring expression to your app with Genmoji”) opisuje pełny proces tworzenia.

Jak to współgra z Writing Tools?

Writing Tools (omówione w Writing Tools API) zachowuje adaptive image glyphs w wynikach przepisywania. Użytkownik, który zaznaczy tekst zawierający Genmoji i poprosi Writing Tools o przepisanie, otrzyma wynik zachowujący Genmoji w semantycznie odpowiednich miejscach. Aplikacje uczestniczące w Writing Tools poprzez UIWritingToolsCoordinator muszą poprawnie przeprowadzać instancje NSAdaptiveImageGlyph przez własną pamięć tekstową w obie strony.

Jaka jest różnica między NSAdaptiveImageGlyph a NSTextAttachment?

NSTextAttachment to starszy, szerszy system załączników do treści nietekstowych (obrazów, plików, własnych rysunków) w tekście atrybutowanym. NSAdaptiveImageGlyph to specjalizacja iOS 18 dla obrazów w stylu emoji, które dostosowują się do charakterystyki otaczającej czcionki. Oba są dołączane przez atrybuty ciągu atrybutowanego, ale używają różnych kluczy (.attachment vs .adaptiveImageGlyph) i różnych ścieżek renderowania (TextKit 1+TextKit 2 vs wyłącznie TextKit 2). Nowy kod celujący w treści w stylu Genmoji korzysta z NSAdaptiveImageGlyph.

Źródła


  1. Dokumentacja Apple Developer: supportsAdaptiveImageGlyph. Właściwość włączająca obsługę, zadeklarowana w protokole UITextInput, z którym UITextView jest zgodne; ta sama właściwość jest więc dostępna jako textView.supportsAdaptiveImageGlyph

  2. Dokumentacja Apple Developer: NSAdaptiveImageGlyph. Typ danych opakowujący zawartość obrazu, identyfikator, opis i typ treści. 

  3. Dokumentacja Apple Developer: NSAttributedString.Key.adaptiveImageGlyph i NSAttributedString(adaptiveImageGlyph:attributes:). Powierzchnia integracji adaptive image glyphs z ciągiem atrybutowanym. 

  4. Dokumentacja Apple Developer: TextKit i Using TextKit 2 to interact with text. Aktualne punkty wejścia TextKit 2; renderowanie adaptive image glyph zależy od architektury układu TextKit 2. 

  5. Dokumentacja Apple Developer: NSAttributedString.DocumentType. Obsługiwane formaty tekstu sformatowanego (RTFD, HTML itd.) dla zachowania adaptive image glyphs przy utrwalaniu w obie strony. 

Powiązane artykuły

Image Playground API: arkusz SwiftUI, programowy kreator obrazów i kontrola stylu

Image Playground daje aplikacjom dwie ścieżki: modyfikator imagePlaygroundSheet w SwiftUI oraz programowe API ImageCreat…

8 min czytania

Framework Translation od Apple: darmowy, działający na urządzeniu i lepszy, niż się wydaje

Framework Translation od Apple: darmowe tłumaczenie na urządzeniu przez translationPresentation i TranslationSession, pl…

8 min czytania

Loop Engineering: Loops Win Where Verification Is Cheap

Loop engineering, checked against Boris Cherny's full transcripts: every loop he names has cheap verification. That cons…

19 min czytania