← 所有文章

Genmoji与NSAdaptiveImageGlyph:应用如何显示用户生成的内联表情

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

本文对照Apple文档梳理API。立足点是”现有的文本处理应用要正确显示Genmoji必须做什么”,因为大多数在UITextView中接收用户输入文本的应用都需要启用自适应图像字形才能渲染用户的Genmoji,而持久化方面也存在容易被忽视的序列化问题。

摘要

  • NSAdaptiveImageGlyph(iOS 18+)是一种数据类型,封装了自适应图像及其标识元数据。从系统键盘输入的Genmoji会以NSAdaptiveImageGlyph实例的形式嵌入富文本2
  • supportsAdaptiveImageGlyph声明在UITextInput协议上;UITextView遵循该协议,因此可通过textView.supportsAdaptiveImageGlyph = true设置该属性。默认值为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
// Also requires TextKit 2 (default on UITextView for iOS 16+
// when constructed via Interface Builder or modern initializer)

未设置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 2。新代码无需额外操作即可获得支持。

仍在使用TextKit 1的旧应用。 需要进行迁移。对于子类化NSLayoutManager、覆盖布局相关代理方法或直接使用旧版NSTextStorage的应用,TextKit 2迁移并不简单。Apple的TextKit迁移指南涵盖了迁移路径;对于使用方式简单的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的富文本+附件格式。可以往返保留自适应图像字形。备忘录、邮件(发送富内容时)以及TextEdit都使用此格式。该格式较为冗长(一个包含附件的目录包),但是无损的。

带嵌入图像的HTML。 适合Web。字形会序列化为带base64编码data URI的<img>标签。负载较大,但适用于大多数支持富文本的接收方。

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

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

常见故障

来自Genmoji集成日志的三种模式:

忘记设置supportsAdaptiveImageGlyph = true 最常见的bug。文本视图能正常渲染Unicode表情,但Genmoji显示为占位符字符。修复方法:在每个接收用户输入文本的UITextView/NSTextView上将该属性设置为true。对于SwiftUI,使用.supportsAdaptiveImageGlyph(true)修饰符。

纯文本持久化剥离字形。 将文本视图的内容保存为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;Web兼容存储用带嵌入图像的HTML;高性能应用用带附件旁路通道的自定义二进制格式。对于用户会输入Genmoji的应用,纯文本是错误的默认选择。

完整的Apple生态系统系列:类型化App IntentsMCP服务器路由问题Foundation Models运行时与工具LLM之分三种界面单一数据源模式两个MCP服务器Apple开发的钩子机制实时活动watchOS运行时SwiftUI内部机制RealityKit的空间心智模型SwiftData模式纪律Liquid Glass模式多平台发布平台矩阵Vision框架Symbol EffectsCore ML推理Writing Tools APISwift Testing隐私清单作为平台的无障碍SF Pro字体排印visionOS空间模式Speech框架SwiftData迁移tvOS焦点引擎@Observable内部机制SwiftUI Layout协议自定义SF SymbolsAVFoundation HDRwatchOS锻炼生命周期iOS 26中的App Intents 2.0Image Playground API我拒绝写的内容。汇总页见Apple生态系统系列。如需更广泛的iOS与AI智能体相关背景,请参阅iOS智能体开发指南

常见问题

我只用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自适应图像数据。contentDescriptioncontentIdentifiercontentType是从编码数据中读取的,而不是作为单独的参数传入;创建自定义自适应图像字形的应用需要准备好嵌入元数据的HEIC负载,然后从该数据构造字形。WWDC 2024场次10220(”通过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开发者文档:TextKit 2迁移指南。从旧版TextKit 1布局引擎迁移到TextKit 2的路径,是渲染自适应图像字形的必要条件。 

  5. Apple开发者文档:NSAttributedString.DocumentType。支持自适应图像字形通过持久化往返转换的富文本格式(RTFD、HTML等)。 

相关文章

Image Playground API: SwiftUI Sheet, Programmatic Image Creator, And Style Control

Image Playground gives apps two paths: the SwiftUI imagePlaygroundSheet modifier and the programmatic ImageCreator API f…

11 分钟阅读

Writing Tools API: How Apps Plug Into Apple Intelligence's Writing Layer

iOS 18 introduced Writing Tools. iOS 26 makes adoption a single property for standard text views, and a designed coordin…

14 分钟阅读

The Cleanup Layer Is the Real AI Agent Market

Charlie Labs pivoted from building agents to cleaning up after them. The AI agent market is moving from generation to pr…

15 分钟阅读