NSTextAttachment:是什么以及如何在iOS中向文本添加图像

作者: IT Sectr 发布日期: 2026-07-25 阅读时间: 9 分钟

NSTextAttachment 是 UIKit 框架中的一个类,允许通过 NSAttributedString 将图像和其他媒体对象嵌入到格式化文本中。它在 TextKit 和 Core Text 级别工作,使开发人员能够控制文本流中附件的大小、位置和行为。根据 Apple Documentation (2025),NSTextAttachment 支持图像、PDF 和自定义视图容器,并与 UITextView 和 UILabel 集成。理解这个 API 对于在 iOS 应用中创建富文本至关重要,从聊天和编辑器到带有图标说明的新闻源。

要点

  • NSTextAttachment — 用于在格式化 iOS 文本中嵌入媒体的 UIKit 类。
  • 图像通过 NSAttributedString 使用 NSAttachmentAttributeName 键添加。
  • bounds 属性控制附件的大小和垂直偏移。
  • NSTextAttachment 支持 图像、PDF 以及通过数据容器自定义文件。
  • API 与 UITextView、UILabel 和 TextKit 集成,实现灵活的文本布局。

什么是 NSTextAttachment?

NSTextAttachment 是 UIKit 框架中的一个类,允许将媒体对象直接嵌入到 NSAttributedString 的文本内容中。与放置在文本旁边的独立 UIImageView 不同,NSTextAttachment 使图像成为文本流的一部分:图像随行移动,参与对齐,并像普通字符一样占据空间。

该类是 TextKit 架构的一部分,该架构管理 iOS 和 macOS 上的文本渲染。TextKit 将文本分解为字形,考虑字距、连字和行距,而 NSTextAttachment 作为特殊字符加入此管道,渲染为图形对象而不是字母。

每个 NSTextAttachment 实例包含 contents(文件数据)、fileType(内容的 UTI 类型)和 bounds(以 points 为单位定义大小和位置的矩形)属性。根据 Apple Human Interface Guidelines,bounds 默认为图像大小,但可以更改以精确匹配字体。

当图像应该作为文本的一部分时,使用 NSTextAttachment 而不是单独的 UIImageView,在聊天、新闻源、文本编辑器和输入字段中带有图标的表单中。

如何通过 NSTextAttachment 向文本添加图像

使用 NSTextAttachment 的基本场景包括三个步骤:创建实例、设置图像和附加到 NSAttributedString。让我们通过 Swift 示例来看这个过程。

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

调用 NSAttributedString(attachment:) 后,NSTextAttachment 对象转换为 NSAttachmentAttributeName 属性,该属性附加到一个特殊的替换字符(对象替换字符,代码 0xFFFC)上。该字符不显示为字母,而是绘制来自 image 属性的图像。

如果图像应位于文本行的中间(例如单词后的图标),只需将 attachmentString 插入到 NSMutableAttributedString 中的所需位置。TextKit 将自动考虑行高并将图像对齐到基线。

重要提示:image 属性仅在 iOS 上可用(从 iOS 7 开始)。在 macOS 上使用 contents 属性和 NSImage。为了向后兼容,始终直接设置 image,不要依赖数据容器。

配置 bounds 和附件位置

默认情况下,NSTextAttachment 以原始大小(以 points 为单位)显示图像。在实践中,几乎总是需要调整大小和垂直位置,为此使用 bounds 属性。

CGRect bounds 结构包括 origin(X 和 Y 上的偏移)和 size(宽度和高度)。Y 上的偏移尤其重要:负值将图像置于基线下方,正值将其抬高。典型场景是将图标对齐到文本行的中心。

swift
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
)

对齐计算使用字体的 capHeight(大写字母的高度,而不是完整的行高)。这确保图标在视觉上与文本的上边界对齐,不会在基线和上升部之间晃动。示例中的图像大小为 18×18 pt,这是 UI 文本中图标的典型尺寸。

如果图像应大于文本行(例如聊天中的照片预览),TextKit 将自动增加当前行的行间距。在这种情况下,bounds 设置为自然大小,Y 偏移为零。

PDF 和自定义附件

NSTextAttachment 不仅支持 PNG 和 JPEG,还支持 PDF 文档以及通过 contents 属性支持任意文件。这使其成为 iOS 应用中富文本的通用工具。

attachment.image 中设置 PDF 图像时,系统会自动渲染文档的第一页。但是,要精确显示,请通过使用 data 和 UTI 类型的初始化器直接使用数据:

swift
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)

ofType 参数接受 UTI 字符串(统一类型标识符)。标准类型:public.image(任何图像)、public.jpeg、public.png、com.adobe.pdf。在指定正确的 UTI 时,系统会选择适当的渲染方法,对于 PDF 是 CGPDFDocument,对于图像是 CGImageSource。

对于自定义文件类型(例如 SVG 格式的矢量图形),需要 NSTextAttachment 的子类来重写 image(forBounds:textContainer:characterIndex:) 方法。如果标准渲染不合适,返回 nil 并通过 Core Graphics 在 draw 方法中实现绘制。

UITextView 中的 NSTextAttachment 和委托

NSTextAttachment 在 UILabelUITextView 中都能正常工作。然而,UITextView 提供了更多功能:与附件交互(点击图标)、编辑文本以及图像和支持 NSAttachmentBehavior。

为了处理 UITextView 中 NSTextAttachment 的点击,使用 UITextViewDelegate 委托和 textView(_:shouldInteractWith:in:interaction:) 方法。当用户点击附件时调用此方法,允许导航到屏幕、打开弹出窗口或播放动画。

swift
func textView(_ textView: UITextView,
                shouldInteractWith attachment: NSTextAttachment,
                in characterRange: NSRange,
                interaction: UITextItemInteraction) -> Bool {
    if interaction == .preview {
        return false
    }
    // 打开详情屏幕
    showImageDetail(attachment.image)
    return false
}

该方法通过 interaction 参数区分交互类型:.preview(3D Touch / Haptic Touch)、.default(普通点击)和 .presentActions(上下文菜单)。对于每种类型,您可以设置自己的行为或通过返回 false 禁用它。

在编辑带有 NSTextAttachment 的 UITextView 时,重要的是要记住:删除替换字符(0xFFFC)也会删除附件。用户将图像视为一个整体元素,整体选中,而不是逐像素。为了在 iOS 15+ 中支持附件的拖放,使用 NSTextAttachmentViewProvider。

使用 NSTextAttachment 的常见错误

第一个常见错误 — 忽略 bounds。开发人员经常依赖图像的原始大小,导致文本中出现巨大的图标,或者相反,几乎看不见的图像。始终显式设置 bounds,考虑字体和上下文。

第二个错误 — Y 偏移不正确。bounds.origin.y 中的正值会将图像向下移动(在 UIKit 坐标系中,Y 轴向下,这似乎不合逻辑,但 Core Graphics 就是这样工作的)。要居中到行的中心,使用字体的 capHeight 公式,如第 3 节所示。

第三个问题 — 在 traitCollection 改变时丢失图像。当用户切换深色模式或动态类型时,字体大小可能会改变,而 bounds 保持不变。解决方案 — 在 layoutSubviews 方法中或通过 font 上的 KVO 动态计算 bounds。

第四个常见错误 — 在 UILabel 中使用 numberOfLines > 1 的 NSTextAttachment。在多行模式和有限宽度下,TextKit 正确地将行与图像一起移动。当附件高度超过行高时出现问题,相邻行会重叠。解决方案 — 通过 NSMutableParagraphStyle 增加 lineSpacing。

常见问题

NSTextAttachment 和 UIImageView 在文本中有什么区别?

NSTextAttachment 使图像成为文本流的一部分:它随文本移动、对齐和缩放。UIImageView 绑定到父视图的坐标,不参与文本布局。

可以将 NSTextAttachment 添加到 UILabel 吗?

可以,如果设置 attributedText 属性而不是 text。UILabel 正确显示 NSTextAttachment,但不支持交互性。点击附件使用 UITextView。

创建 attachment 后如何更改图像大小?

更改现有 NSTextAttachment 实例的 bounds 属性。文本将自动使用新大小重新渲染,无需创建新的 NSAttributedString。

NSTextAttachment 支持动画图像吗?

NSTextAttachment 支持静态图像。对于 GIF 和动画格式,需要通过 NSTextAttachmentViewProvider(在 iOS 15+ 中)或通过 CADisplayLink 进行自定义实现。

如何以编程方式向文本添加 NSTextAttachment?

创建 NSTextAttachment 实例,设置 image 或 data,设置 bounds,包装到 NSAttributedString(attachment:) 中,并通过 insert(_:at:) 方法插入到 NSMutableAttributedString 中。

总结

  • NSTextAttachment — 用于将图像、PDF 和其他媒体嵌入格式化文本的 UIKit 类。
  • 图像通过 NSAttributedString(attachment:) 添加,并成为文本流的一部分。
  • bounds 属性控制大小和位置:始终显式设置,考虑字体。
  • 要将图标对齐到行的中心,使用当前字体的 capHeight 公式。
  • PDF 附件通过带有 data 和正确 UTI 类型的初始化器支持。
  • 要处理附件点击,使用 UITextViewDelegate 和 shouldInteractWith 方法。
  • 在动态类型更改时动态重新计算 bounds,以避免行重叠。

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读