← 所有文章

Genmoji与NSAdaptiveImageGlyph:iOS 18+上的内嵌表情

Genmoji是Apple Intelligence自定义表情功能的面向用户名称:用户输入描述,系统生成一张内嵌的类表情图像,结果与Unicode表情一同出现在文本中。面向开发者的接口是NSAdaptiveImageGlyph,这是iOS 18+引入的类,用于在富文本中表示自适应内嵌图像1。Genmoji是NSAdaptiveImageGlyph实例的来源之一;处理富文本的应用需要支持该类,才能显示Genmoji(以及Apple未来推出的任何自适应图像字形内容)。

TL;DR

  • 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应用通过UIViewRepresentable包装UITextView,并在底层视图上设置supportsAdaptiveImageGlyph = true。诸如GlyphMeThat这样的社区封装提供了现成的桥接方案。

TextKit 2是关键支撑

NSAdaptiveImageGlyph需要TextKit 2的布局架构4。TextKit 1(即随原始NSTextStorage/NSLayoutManager/NSTextContainer模型一同发布的旧版文本引擎)无法正确渲染自适应图像字形;字形会显示为通用占位符,或完全无法布局。

应用可分为三种状态:

iOS 18+上的新应用。 默认使用TextKit 2。在iOS 16+上,通过Interface Builder或init(frame:textContainer:)初始化的UITextView默认使用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的富文本+附件格式。可往返保留自适应图像字形。被备忘录、邮件(发送富内容时)和文本编辑使用。该格式较为冗长(一个包含附件的目录捆绑包),但无损。

带嵌入图像的HTML。 适合网络环境。字形序列化为带base64编码data URI的<img>标签。负载更大,但在大多数支持富文本的接收方都能工作。

带扩展的Markdown。 标准Markdown没有自适应图像字形语法,但扩展方言(带附件支持的CommonMark、Apple自有的扩展Markdown)可以承载它们。对于任何基于Markdown的持久化方案,请记录其方言要求。

通过网络发送文本(聊天、邮件、社交媒体)的应用需要决定:端到端保留字形(仅当发送方和接收方都为iOS 18+且传输支持富文本时才有效)、剥离字形并替换为后备文本(contentDescription),或将字形渲染为系统图像并嵌入该图像。正确的选择取决于受众和平台。

常见失败

NSAdaptiveImageGlyph采用过程中反复出现三种失败模式:

忘记设置supportsAdaptiveImageGlyph = true 最常见的bug。文本视图能正常渲染Unicode表情,但Genmoji显示为占位符字符。修复方式:在每一个UITextView(UIKit)或任何NSTextInputClient遵循者(如AppKit中的NSTextView)上将该属性设为true。SwiftUI没有原生修饰符;通过UIViewRepresentable(macOS上为NSViewRepresentable)包装UITextView,并在底层视图上设置该属性。

纯文本持久化剥离字形。 将文本视图的内容保存为text(纯String)会丢弃Genmoji。用户输入自定义表情,在文本视图中看到它,保存文档,重新打开后;表情消失了。修复方式:以attributedText形式持久化,使用支持自适应图像字形的富文本格式(RTFD、HTML、带附件旁路通道的自定义格式)。

网络传输静默丢弃字形。 一个将外发消息序列化为纯文本的消息应用在发送时会剥离Genmoji。接收方看到的是占位符字符或空白。修复方式:要么发送富内容(并确保接收方支持),要么对纯文本接收方替换为contentDescription并将图像数据作为单独附件包含。

这一模式对iOS 18+应用意味着什么

三个要点。

  1. 在每一个文本输入上设置supportsAdaptiveImageGlyph = true 接受用户输入文本的应用对自适应图像字形默认采用主动启用模式。这一个属性就是Genmoji正常渲染与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 Agent开发指南

FAQ

只要使用UITextView,我的应用就能”免费”获得Genmoji吗?

并非完全如此。UITextView.supportsAdaptiveImageGlyph的默认值为false。应用必须将该属性设置为true来主动启用。启用后,系统键盘的Genmoji标签页会向用户显示,粘贴的Genmoji也能正确渲染。如果不主动启用,在其他位置输入并粘贴到文本视图中的Genmoji会显示为占位符字符。

测试Genmoji需要启用Apple Intelligence吗?

完整的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 session 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 Developer Documentation:supportsAdaptiveImageGlyph。声明于UITextInput协议(UITextView遵循该协议)的主动启用属性;因此同一属性也可通过textView.supportsAdaptiveImageGlyph访问。 

  2. Apple Developer Documentation:NSAdaptiveImageGlyph。封装图像内容、标识符、描述和内容类型的数据类型。 

  3. Apple Developer Documentation:NSAttributedString.Key.adaptiveImageGlyphNSAttributedString(adaptiveImageGlyph:attributes:)。自适应图像字形的富文本字符串集成接口。 

  4. Apple Developer Documentation:TextKitUsing TextKit 2 to interact with text。当前的TextKit 2入口点;自适应图像字形渲染依赖于TextKit 2的布局架构。 

  5. Apple Developer Documentation:NSAttributedString.DocumentType。用于通过持久化往返传递自适应图像字形的支持的富文本格式(RTFD、HTML等)。 

相关文章

Image Playground API:SwiftUI Sheet、编程式 Image Creator 与样式控制

Image Playground 为应用提供两条路径:SwiftUI 的 imagePlaygroundSheet 修饰符,以及用于从概念生成图像的编程式 ImageCreator API。

3 分钟阅读

Apple 的 Translation 框架:免费、设备端运行,且比看上去更出色

Apple 的 Translation 框架:通过 translationPresentation 与 TranslationSession 实现免费的设备端翻译,以及演示中略过的离线下载细节。

2 分钟阅读

循环工程:在验证成本低廉处,循环才能取胜

循环工程,对照 Boris Cherny 的完整访谈记录来检验:他点名的每一个循环,验证成本都很低。正是这一约束决定了什么值得自动化。

4 分钟阅读