← 所有文章

Genmoji 與 NSAdaptiveImageGlyph:iOS 18+ 的行內表情符號

Genmoji 是 Apple Intelligence 自訂表情符號功能的使用者端名稱:使用者輸入描述,系統便會生成一個行內的類表情符號圖像,結果會與 Unicode 表情符號一同出現在文字中。對應的開發者端介面是 NSAdaptiveImageGlyph,這是 iOS 18+ 的類別,用來表示屬性化文字中的自適應行內圖像1。Genmoji 是 NSAdaptiveImageGlyph 實例的來源之一;處理屬性化文字的應用程式必須支援此類別,才能顯示 Genmoji(以及未來 Apple 推出的任何自適應圖像字符內容)。

重點摘要

  • NSAdaptiveImageGlyph(iOS 18+)是一種資料型別,封裝自適應圖像與識別中介資料。從系統鍵盤輸入的 Genmoji 會以 NSAdaptiveImageGlyph 實例的形式嵌入屬性化文字中2
  • supportsAdaptiveImageGlyph 宣告於 UITextInput 協定;UITextView 遵循該協定,因此可透過 textView.supportsAdaptiveImageGlyph = true 設定此屬性。Apple 的說明相當直接:當該屬性為 false 時,「輸入系統不允許文字輸入包含自適應圖像。」應用程式必須選擇加入,Genmoji 才會渲染。
  • 自適應圖像字符必須使用 TextKit 2。仍使用 TextKit 1 的應用程式無法正確渲染 NSAdaptiveImageGlyph。以 iOS 18+ 為目標的新應用程式應預設採用 TextKit 2。
  • NSAttributedString 透過 NSAttributedString.Key.adaptiveImageGlyph 屬性攜帶自適應圖像字符。初始化器 NSAttributedString(adaptiveImageGlyph:attributes:) 可建構僅包含單一字符的屬性化字串3
  • 持久化與往返序列化需要謹慎處理。純文字儲存會完全剝除 Genmoji;富文字格式(RTFD、含擴充功能的 Markdown、含嵌入圖像資料的 HTML)則會保留它們。

NSAdaptiveImageGlyph 攜帶哪些內容

NSAdaptiveImageGlyph 是一個資料封裝物件,擁有四個識別屬性2:

  • imageContent: Data 圖像資料本身,格式由 contentType 宣告。
  • contentIdentifier: String 字符實例的唯一識別碼。用於去重以及系統內部快取。
  • contentDescription: String 描述字符的替代文字。需要呈現無障礙標籤,或將字符傳送給不支援字符的接收端的應用程式會使用此屬性。
  • contentType: UTType 一個類別層級的型別屬性,公開 Apple 用於自適應字符的圖像格式(HEIC 的變體)。進行序列化的應用程式會檢查此屬性,以驅動格式感知的處理邏輯。

對於標準的 Genmoji,資料通常為數十 KB。多種尺寸利用 HEIC 的自適應圖像功能編碼於同一個圖像檔案中;系統會根據渲染情境選擇正確的尺寸。

UITextView 中啟用自適應圖像字符

選擇加入只需設定一個屬性1:

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)

若未設定 supportsAdaptiveImageGlyph = true,使用者輸入的 Genmoji 會顯示為佔位字元(系統無法渲染該字符)。設定此屬性後,即可啟用渲染功能,並讓系統鍵盤顯示「Genmoji」分頁,使用者便能在文字檢視中建立自訂 Genmoji。

SwiftUI 原生的 TextFieldTextEditor 目前並未提供 supportsAdaptiveImageGlyph 修飾器。需要自適應圖像字符渲染的 SwiftUI 應用程式會將 UITextView 包裝於 UIViewRepresentable 中,並在底層檢視上設定 supportsAdaptiveImageGlyph = true。社群提供的封裝套件如 GlyphMeThat 已備好現成的橋接方案。

TextKit 2 是不可或缺的基礎

NSAdaptiveImageGlyph 需要 TextKit 2 的版面配置架構4。TextKit 1(隨原始 NSTextStorage/NSLayoutManager/NSTextContainer 模型推出的舊版文字引擎)無法正確渲染自適應圖像字符;字符會顯示為通用佔位符,或完全無法配置版面。

應用程式可分為三種狀態:

iOS 18+ 上的新應用程式。 預設採用 TextKit 2。透過 Interface Builder 或 init(frame:textContainer:) 初始化的 UITextView 在 iOS 16+ 上預設使用 TextKit 24。新程式碼可直接享有此優勢。

仍使用 TextKit 1 的舊應用程式。 必須進行遷移。對於繼承 NSLayoutManager、覆寫版面相關委派方法,或直接使用舊版 NSTextStorage 的應用程式而言,TextKit 2 的遷移絕非易事。Apple 的 TextKit 2 文件涵蓋了現代版面配置架構;對於僅簡單使用 UITextView 的應用程式,遷移過程大多可自動完成。

混合型應用程式。 部分應用程式會嵌入 WKWebView 處理 HTML 編輯,同時使用 UITextView 進行純文字編輯。WKWebView 透過自身的渲染路徑(非 TextKit)處理自適應圖像字符,因此混合型應用程式可能會出現一個介面支援 Genmoji,而另一個則不支援的情況。請記錄此行為;當一個編輯器支援自訂表情符號而另一個會剝除它們時,使用者會察覺到差異。

NSAttributedString 的整合

自適應圖像字符透過 NSAttributedString.Key.adaptiveImageGlyph 屬性流經屬性化字串3:

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!"))

此模式可組合運用:文字中嵌入字符,再嵌入更多文字。系統會自動處理版面配置(包含字符根據周圍文字字型進行的自適應尺寸調整)。

讀取時,迭代 NSAttributedString.adaptiveImageGlyph 屬性會在字符出現的位置回傳 NSAdaptiveImageGlyph 實例:

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

需要過濾、轉換或持久化文字的應用程式可利用此列舉方式找出字符,並決定如何處理它們。

持久化與序列化

純文字儲存(String、UTF-8 檔案)無法保留自適應圖像字符。在屬性化文字中代表字符的 Unicode 佔位字元會被序列化為 U+FFFC(物件替換字元)或完全消失,實際的字符資料便會遺失。

若要進行可往返的持久化,應用程式需要採用富文字格式5:

RTFD。 Apple 的富文字加附件格式。可往返保留自適應圖像字符。Notes、Mail(傳送富內容時)以及 TextEdit 都使用此格式。雖然該格式較為冗長(包含附件的目錄套件),但無損。

含嵌入圖像的 HTML。 對網頁友善。字符會序列化為帶有 base64 編碼資料 URI 的 <img> 標籤。承載量較大,但可在大多數支援富文字的接收端正常運作。

含擴充功能的 Markdown。 標準 Markdown 並無自適應圖像字符語法,但擴充方言(支援附件的 CommonMark、Apple 自家的擴充 Markdown)可承載這類內容。對任何基於 Markdown 的持久化方案,請記錄方言需求。

透過網路傳送文字的應用程式(聊天、電子郵件、社群媒體)需要決定:端對端保留字符(僅在傳送端與接收端皆為 iOS 18+ 且傳輸協定支援富文字時可行)、剝除字符並以替代文字(contentDescription)取代,或將字符渲染為系統圖像並嵌入該圖像。正確選擇取決於受眾與平台。

常見的失敗情境

NSAdaptiveImageGlyph 採用過程中,有三種失敗模式反覆出現:

忘記設定 supportsAdaptiveImageGlyph = true 最常見的錯誤。文字檢視可正常渲染 Unicode 表情符號,但 Genmoji 卻顯示為佔位字元。修正方式:在每個 UITextView(UIKit)或任何 NSTextInputClient 遵循者(如 AppKit 中的 NSTextView)上將該屬性設為 true。SwiftUI 並無原生修飾器;請將 UITextView 包裝於 UIViewRepresentable(macOS 上則為 NSViewRepresentable),並在底層檢視上設定該屬性。

純文字持久化導致字符遭剝除。 將文字檢視內容儲存為 text(純 String)會丟棄 Genmoji。使用者輸入自訂表情符號,在文字檢視中看到它,儲存文件後重新開啟,表情符號便消失了。修正方式:以 attributedText 搭配支援自適應圖像字符的富文字格式(RTFD、HTML、含附件側通道的自訂格式)進行持久化。

網路傳輸時無聲地丟棄字符。 將傳出訊息序列化為純文字的訊息應用程式會在傳送時剝除 Genmoji。接收方會看到佔位字元或空白。修正方式:傳送富內容(並確保接收端支援),或為純文字接收端以 contentDescription 取代,並將圖像資料作為獨立附件一併傳送。

此模式對 iOS 18+ 應用程式的意義

三項要點。

  1. 在每個文字輸入欄位上設定 supportsAdaptiveImageGlyph = true 接受使用者輸入文字的應用程式應預設啟用自適應圖像字符。這個單一屬性決定了 Genmoji 是能正常渲染或損壞。

  2. 若仍在使用 TextKit 1,請遷移至 TextKit 2。 TextKit 1 已進入維護模式。新的 iOS 26 時代功能(自適應圖像字符、Writing Tools 的行內改寫、Liquid Glass 文字渲染)全都假設使用 TextKit 2。遷移成本確實存在,但替代方案是基於已棄用的文字引擎發布應用程式。

  3. 選擇持久化格式時,請考量自適應圖像字符。 原生 iOS 儲存採用 RTFD;網頁相容儲存採用含嵌入圖像的 HTML;高效能應用程式採用含附件側通道的自訂二進位格式。對於使用者會輸入 Genmoji 的應用程式,純文字是錯誤的預設選擇。

完整的 Apple 生態系列文章:Writing Tools 介紹平行的 Apple Intelligence 文字介面;Image Playground 涵蓋圖像生成;App Intents 2.0 探討 iOS 26 Apple Intelligence 整合;Foundation Models 介紹裝置端 LLM。集中入口位於 Apple 生態系列。如需更廣泛的「iOS 搭配 AI 代理」相關背景,請參閱 iOS 代理開發指南

常見問答

只要使用 UITextView,我的應用程式就能「免費」獲得 Genmoji 嗎?

並非如此。UITextView.supportsAdaptiveImageGlyph 預設為 false。應用程式必須將該屬性設為 true 才能選擇加入。一旦啟用,系統鍵盤的 Genmoji 分頁便會出現供使用者使用,而貼上的 Genmoji 也能正確渲染。若未選擇加入,從其他位置輸入並貼到文字檢視中的 Genmoji 會顯示為佔位字元。

我需要啟用 Apple Intelligence 才能測試 Genmoji 嗎?

要進行完整的 Genmoji 建立流程,是的。使用者端的 Genmoji 建立流程需要支援 Apple Intelligence 的硬體(iPhone 15 Pro 及更新機型、M 系列 Mac)、iOS 18+ 並啟用 Apple Intelligence。若僅是開發階段測試 NSAdaptiveImageGlyph 的渲染,可使用範例圖像資料以程式化方式建構測試字符實例,並在任何 iOS 18+ 裝置或模擬器上驗證文字檢視的渲染結果。

當我將 Genmoji 傳送給使用 iOS 17 的人時,會發生什麼事?

若無能保留字符的富文字傳輸機制,接收者會看到 contentDescription(替代文字)或佔位字元。現代訊息傳遞框架(Apple 的訊息應用程式、近期版本的郵件用戶端)會自動處理回退;自訂協定則需要明確的處理邏輯。

我可以以程式化方式建立 NSAdaptiveImageGlyph 實例嗎?

可以。公開的初始化器是 init(imageContent: Data),接收預先編碼的 HEIC 自適應圖像資料。contentIdentifiercontentDescription 是從編碼承載讀取的實例屬性;contentType 則是描述 Apple 用於自適應字符之格式(HEIC 變體)的類別層級 UTType,並非以實例為單位。建立自訂自適應圖像字符的應用程式需準備好嵌入了個別字符識別碼與描述的 HEIC 承載,再從該資料建構字符。WWDC 2024 第 10220 場議程(「Bring expression to your app with Genmoji」)涵蓋完整的建立流程。

這與 Writing Tools 如何互動?

Writing Tools(於 Writing Tools API 中介紹)會在改寫輸出中保留自適應圖像字符。使用者選取包含 Genmoji 的文字並請求 Writing Tools 進行改寫時,所獲得的改寫結果會在語意適當的位置保留 Genmoji。透過 UIWritingToolsCoordinator 參與 Writing Tools 的應用程式,需要在自訂文字儲存中正確地往返處理 NSAdaptiveImageGlyph 實例。

NSAdaptiveImageGlyphNSTextAttachment 有何差異?

NSTextAttachment 是較舊、用途較廣的附件系統,用於在屬性化文字中嵌入非文字內容(圖像、檔案、自訂繪圖)。NSAdaptiveImageGlyph 則是 iOS 18 針對類表情符號行內圖像的特化版本,可根據周圍字型特性進行調整。兩者皆透過屬性化字串屬性附加,但使用不同的鍵(.attachment.adaptiveImageGlyph),也採用不同的渲染路徑(TextKit 1+TextKit 2 與僅限 TextKit 2)。針對 Genmoji 風格內容的新程式碼應使用 NSAdaptiveImageGlyph

參考資料


  1. Apple 開發者文件:supportsAdaptiveImageGlyph。宣告於 UITextInput 協定的選擇加入屬性,而 UITextView 遵循該協定;因此同一屬性可透過 textView.supportsAdaptiveImageGlyph 存取。 

  2. Apple 開發者文件:NSAdaptiveImageGlyph。封裝圖像內容、識別碼、描述與內容型別的資料型別。 

  3. Apple 開發者文件:NSAttributedString.Key.adaptiveImageGlyphNSAttributedString(adaptiveImageGlyph:attributes:)。自適應圖像字符的屬性化字串整合介面。 

  4. Apple 開發者文件:TextKitUsing TextKit 2 to interact with text。當前的 TextKit 2 進入點;自適應圖像字符渲染需依賴 TextKit 2 的版面配置架構。 

  5. Apple 開發者文件:NSAttributedString.DocumentType。可透過持久化往返保留自適應圖像字符的支援富文字格式(RTFD、HTML 等)。 

相關文章

Image Playground API:SwiftUI 表單、程式化影像產生器與樣式控制

Image Playground 為應用程式提供兩條途徑:SwiftUI 的 imagePlaygroundSheet 修飾符,以及用於依概念產生影像的程式化 ImageCreator API。

3 分鐘閱讀

Apple 的 Translation 框架:免費、裝置端執行,而且比表面更銳利

Apple 的 Translation 框架:透過 translationPresentation 與 TranslationSession 提供免費的裝置端翻譯,以及示範影片略過的離線下載眉角。

2 分鐘閱讀

迴圈工程:在驗證成本低廉之處,迴圈才能取勝

以 Boris Cherny 的完整逐字稿驗證迴圈工程:他點名的每一個迴圈,驗證成本都很低廉。這項限制決定了什麼適合自動化。

4 分鐘閱讀